@@ -119,3 +119,73 @@ const polkadotSigner = getPolkadotSigner(
119119 hdkdKeyPair.sign,
120120)
121121` ` `
122+
123+ #### ` Ecdsa `
124+
125+ Creating an ` Ecdsa ` ` PolkadotSigner ` can be tricky , especially when dealing with different chains like Polkadot and EVM - like chains . Below is some code to illustrate how this can be done effectively .
126+
127+ ##### Chain Differences in Signers
128+
129+ - ** EVM - like chains ** (Moonbeam , Mythos , Darwinia , Crab , etc .) expect the signer to sign payloads using a ** Keccak256 hash ** and use ** AccountId20 ** addresses (Ethereum - like addresses ).
130+ - ** Polkadot - like chains ** (e .g ., Polkadot , Kusama ) expect the signer to sign payloads using ** Blake2_256 ** and use ** AccountId32 ** addresses (Polkadot - like addresses ).
131+
132+ With that distinction in mind , here ' s how you can create `Ecdsa` `PolkadotSigner`s for these different chain types:
133+
134+ :::warning
135+ The following code is for illustrative purposes only . It stores private keys in memory , which is not ideal from a security standpoint . You should refactor the code to meet the security standards of your environment .
136+ :::
137+
138+ ` ` ` ts
139+ import { mnemonicToSeedSync } from "@scure/bip39"
140+ import { HDKey } from "@scure/bip32"
141+ import { getPolkadotSigner, type PolkadotSigner } from "polkadot-api/signer"
142+ import { secp256k1 } from "@noble/curves/secp256k1"
143+ import { keccak_256 } from "@noble/hashes/sha3"
144+ import { blake2b256 } from "@noble/hashes/blake2b"
145+
146+ const signEcdsa = (
147+ hasher: (input: Uint8Array) => Uint8Array,
148+ value: Uint8Array,
149+ priv: Uint8Array,
150+ ) => {
151+ const signature = secp256k1.sign(hasher(value), priv)
152+ const signedBytes = signature.toCompactRawBytes()
153+
154+ const result = new Uint8Array(signedBytes.length + 1)
155+ result.set(signedBytes)
156+ result[signedBytes.length] = signature.recovery
157+
158+ return result
159+ }
160+
161+ // A signer for EVM like chains that use AccountId20 as their public address
162+ const getEvmEcdsaSigner = (privateKey: Uint8Array): PolkadotSigner => {
163+ const publicAddress = keccak_256(
164+ secp256k1.getPublicKey(privateKey, false).slice(1),
165+ ).slice(-20)
166+
167+ return getPolkadotSigner(publicAddress, "Ecdsa", (iput) =>
168+ signEcdsa(keccak_256, input, privateKey),
169+ )
170+ }
171+
172+ const getEvmEcdsaSignerFromMnemonic = (
173+ mnemonic: string,
174+ accountIdx: number = 0,
175+ password: string = "",
176+ ): PolkadotSigner => {
177+ const seed = mnemonicToSeedSync(mnemonic, password)
178+ const keyPair = HDKey.fromMasterSeed(seed).derive(
179+ ` m / 44 ' /60' / 0 ' /0/${accountIdx}`,
180+ )
181+ return getEvmEcdsaSigner (keyPair .privateKey ! )
182+ }
183+
184+ // A signer for Polkadot like chains that use AccountId32 as the public address
185+ const getPolkadotEcdsaSigner = (privateKey : Uint8Array ): PolkadotSigner =>
186+ getPolkadotSigner (
187+ blake2b (secp256k1 .getPublicKey (privateKey ), { dkLen: 32 }),
188+ " Ecdsa" ,
189+ (input ) => signEcdsa ((i ) => blake2b (i , { dkLen: 32 }), input , privateKey ),
190+ )
191+ ```
0 commit comments