@@ -119,3 +119,73 @@ const polkadotSigner = getPolkadotSigner(
119
119
hdkdKeyPair.sign,
120
120
)
121
121
` ` `
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