From 7cbed3b41b40a3d745c70849714c46d414b2e8d0 Mon Sep 17 00:00:00 2001 From: Jayasudha Jayakumaran Date: Thu, 12 Sep 2024 10:44:45 -0700 Subject: [PATCH 1/7] fix arb --- src/coinbase/wallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coinbase/wallet.ts b/src/coinbase/wallet.ts index 96b74c1f..836c49ac 100644 --- a/src/coinbase/wallet.ts +++ b/src/coinbase/wallet.ts @@ -918,7 +918,7 @@ export class Wallet { * TODO: Push this logic to the backend. * TODO: Add unit tests for `#createAddress`. */ - if (!["base", "ethereum", "polygon"].includes(networkPrefix)) { + if (!["base", "ethereum", "polygon", "arbitrum"].includes(networkPrefix)) { throw new Error(`Unsupported network ID: ${this.model.network_id}`); } const derivedKey = this.master?.derive(this.addressPathPrefix + `/${index}`); From f3bf82c86314ce3c79f14910e32edbf8cdf4bd9d Mon Sep 17 00:00:00 2001 From: Jayasudha Jayakumaran Date: Thu, 12 Sep 2024 10:49:22 -0700 Subject: [PATCH 2/7] update version --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b332568d..48dd67e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +## [0.5.1] - 2024-09-12 + +### Fixed +- Fixed a bug that blocked arbitrum mainnet wallets from being created + ## [0.5.0] - 2024-09-11 - Add Arbitrum-Mainnet support for Native transfers. diff --git a/package.json b/package.json index 74d00f6d..a80fab97 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "ISC", "description": "Coinbase Platform SDK", "repository": "https://github.com/coinbase/coinbase-sdk-nodejs", - "version": "0.5.0", + "version": "0.5.1", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { From 1cf2237f934d52353e61b0d9a2fe3212494b647c Mon Sep 17 00:00:00 2001 From: Jayasudha Jayakumaran Date: Thu, 12 Sep 2024 10:58:49 -0700 Subject: [PATCH 3/7] remove network check logic from sdk --- src/coinbase/wallet.ts | 8 +------- src/tests/wallet_test.ts | 14 -------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/coinbase/wallet.ts b/src/coinbase/wallet.ts index 836c49ac..00412ed4 100644 --- a/src/coinbase/wallet.ts +++ b/src/coinbase/wallet.ts @@ -914,13 +914,7 @@ export class Wallet { throw new Error("Cannot derive key for Wallet without seed loaded"); } const [networkPrefix] = this.model.network_id.split("-"); - /** - * TODO: Push this logic to the backend. - * TODO: Add unit tests for `#createAddress`. - */ - if (!["base", "ethereum", "polygon", "arbitrum"].includes(networkPrefix)) { - throw new Error(`Unsupported network ID: ${this.model.network_id}`); - } + const derivedKey = this.master?.derive(this.addressPathPrefix + `/${index}`); if (!derivedKey?.privateKey) { throw new Error("Failed to derive key"); diff --git a/src/tests/wallet_test.ts b/src/tests/wallet_test.ts index 3a7d5619..6706a5fd 100644 --- a/src/tests/wallet_test.ts +++ b/src/tests/wallet_test.ts @@ -870,20 +870,6 @@ describe("Wallet Class", () => { expect(() => newWallet.setSeed(``)).toThrow(ArgumentError); expect(() => newWallet.setSeed(`invalid-seed`)).toThrow(ArgumentError); }); - - it("should raise an error when creating a wallet with an invalid network", async () => { - const newWallet = Wallet.init( - { - ...walletModel, - network_id: "invalid_network_id", - }, - "", - ); - newWallet.setSeed(existingSeed); - await expect(newWallet.createAddress()).rejects.toThrow( - "Unsupported network ID: invalid_network_id", - ); - }); }); describe("#export", () => { From 42b573eaf4c15f8cab4e926cb1d160155d346942 Mon Sep 17 00:00:00 2001 From: Jayasudha Jayakumaran Date: Thu, 12 Sep 2024 11:06:44 -0700 Subject: [PATCH 4/7] fix coverage --- jest.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jest.config.js b/jest.config.js index 1ce27854..17790c6a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,10 +10,10 @@ module.exports = { maxWorkers: 1, coverageThreshold: { "./src/coinbase/**": { - branches: 78, + branches: 77, functions: 90, statements: 85, - lines: 88, + lines: 87, }, }, }; From 06e29b1e8f04fd4c1a5b9599c4300308f4e0d147 Mon Sep 17 00:00:00 2001 From: Jayasudha Jayakumaran Date: Thu, 12 Sep 2024 11:08:32 -0700 Subject: [PATCH 5/7] fix lint --- src/coinbase/wallet.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coinbase/wallet.ts b/src/coinbase/wallet.ts index 00412ed4..b00abe28 100644 --- a/src/coinbase/wallet.ts +++ b/src/coinbase/wallet.ts @@ -913,7 +913,6 @@ export class Wallet { if (!this.master) { throw new Error("Cannot derive key for Wallet without seed loaded"); } - const [networkPrefix] = this.model.network_id.split("-"); const derivedKey = this.master?.derive(this.addressPathPrefix + `/${index}`); if (!derivedKey?.privateKey) { From 6c71e22fade258496667111e02748c5184416438 Mon Sep 17 00:00:00 2001 From: John Peterson <98187317+John-peterson-coinbase@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:41:00 -0400 Subject: [PATCH 6/7] [PSDK-443] Quickstart Basenames Registration (#243) * base names quickstart * [PSDK-443] Quickstart Register Basenames for MPC Wallet --- quickstart-template/package-lock.json | 162 ++++++++++++++++++++-- quickstart-template/package.json | 8 +- quickstart-template/register-basename.js | 165 +++++++++++++++++++++++ 3 files changed, 322 insertions(+), 13 deletions(-) create mode 100644 quickstart-template/register-basename.js diff --git a/quickstart-template/package-lock.json b/quickstart-template/package-lock.json index fc398e86..18ff1675 100644 --- a/quickstart-template/package-lock.json +++ b/quickstart-template/package-lock.json @@ -1,17 +1,18 @@ { "name": "quickstart-template", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "quickstart-template", - "version": "1.0.0", + "version": "1.1.0", "license": "ISC", "dependencies": { - "@coinbase/coinbase-sdk": "^0.0.13", + "@coinbase/coinbase-sdk": "^0.5.1", "csv-parse": "^5.5.6", - "csv-writer": "^1.6.0" + "csv-writer": "^1.6.0", + "viem": "^2.21.6" } }, "node_modules/@adraffy/ens-normalize": { @@ -20,13 +21,14 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" }, "node_modules/@coinbase/coinbase-sdk": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@coinbase/coinbase-sdk/-/coinbase-sdk-0.0.13.tgz", - "integrity": "sha512-Hl1+7XD2elxnb1PJ/htZr5NV2Gqe6rcHsAplG4lg8QzAnXV0YioA3RwbK7UJszQ9r90suPI2ts9AFadqYO+1CA==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@coinbase/coinbase-sdk/-/coinbase-sdk-0.5.1.tgz", + "integrity": "sha512-z2CdMZV9UvyZ33DZT7HOpz2YxGhjaYVwGEb8izmJfUXm7Rcm6eg3MLL/dizNiOEbDMaO1Mj7LwKqf+tQnDU+Qw==", "dependencies": { "@scure/bip32": "^1.4.0", "axios": "^1.6.8", "axios-mock-adapter": "^1.22.0", + "axios-retry": "^4.4.1", "bip32": "^4.0.0", "bip39": "^3.1.0", "decimal.js": "^10.4.3", @@ -59,9 +61,9 @@ } }, "node_modules/@scure/base": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", - "integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.8.tgz", + "integrity": "sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg==", "funding": { "url": "https://paulmillr.com/funding/" } @@ -79,11 +81,54 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@scure/bip39": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.4.0.tgz", + "integrity": "sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==", + "dependencies": { + "@noble/hashes": "~1.5.0", + "@scure/base": "~1.1.8" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@types/node": { "version": "18.15.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" }, + "node_modules/abitype": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.5.tgz", + "integrity": "sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/aes-js": { "version": "4.0.0-beta.5", "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", @@ -116,6 +161,17 @@ "axios": ">= 0.17.0" } }, + "node_modules/axios-retry": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", + "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", + "dependencies": { + "is-retry-allowed": "^2.2.0" + }, + "peerDependencies": { + "axios": "0.x || 1.x" + } + }, "node_modules/base-x": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", @@ -473,6 +529,31 @@ "node": ">=4" } }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isows": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.4.tgz", + "integrity": "sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "peerDependencies": { + "ws": "*" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -681,6 +762,67 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/viem": { + "version": "2.21.6", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.21.6.tgz", + "integrity": "sha512-YX48IVl6nZ4FRsY4ypv2RrxtQVWysIY146/lBW53tma8u32h8EsiA7vecw9ZbrueNUy/asHR4Egu68Z6FOvDzQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.4.0", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.4.0", + "abitype": "1.0.5", + "isows": "1.0.4", + "webauthn-p256": "0.0.5", + "ws": "8.17.1" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" + }, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/webauthn-p256": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.5.tgz", + "integrity": "sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "dependencies": { + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0" + } + }, "node_modules/wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", diff --git a/quickstart-template/package.json b/quickstart-template/package.json index d8582dcc..3ae6ae92 100644 --- a/quickstart-template/package.json +++ b/quickstart-template/package.json @@ -7,15 +7,17 @@ "start": "node index.js", "start-trade-assets": "node trade-assets.js", "start-mass-payout": "node mass-payout.js", - "start-webhook": "node webhook.js" + "start-webhook": "node webhook.js", + "start-register-basename": "node register-basename.js" }, "keywords": [], "author": "", "license": "ISC", "type": "module", "dependencies": { - "@coinbase/coinbase-sdk": "^0.4.0", + "@coinbase/coinbase-sdk": "^0.5.1", "csv-parse": "^5.5.6", - "csv-writer": "^1.6.0" + "csv-writer": "^1.6.0", + "viem": "^2.21.6" } } diff --git a/quickstart-template/register-basename.js b/quickstart-template/register-basename.js new file mode 100644 index 00000000..9f4e9017 --- /dev/null +++ b/quickstart-template/register-basename.js @@ -0,0 +1,165 @@ +import { Coinbase, Wallet } from "@coinbase/coinbase-sdk"; +import { encodeFunctionData, namehash } from "viem"; +import os from "os"; + +// Relevant ABI for L2 Resolver Contract. +const l2ResolverABI = [ + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "a", type: "address" }, + ], + name: "setAddr", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "string", name: "newName", type: "string" }, + ], + name: "setName", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +// Relevant ABI for Basenames Registrar Controller Contract. +const registrarABI = [ + { + inputs: [ + { + components: [ + { + internalType: "string", + name: "name", + type: "string", + }, + { + internalType: "address", + name: "owner", + type: "address", + }, + { + internalType: "uint256", + name: "duration", + type: "uint256", + }, + { + internalType: "address", + name: "resolver", + type: "address", + }, + { + internalType: "bytes[]", + name: "data", + type: "bytes[]", + }, + { + internalType: "bool", + name: "reverseRecord", + type: "bool", + }, + ], + internalType: "struct RegistrarController.RegisterRequest", + name: "request", + type: "tuple", + }, + ], + name: "register", + outputs: [], + stateMutability: "payable", + type: "function", + }, +]; + +// Basenames Registrar Controller Contract Address. +const BaseNamesRegistrarControllerAddress = "0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5"; + +// Create register contract method arguments. +function createRegisterContractMethodArgs(baseName, addressId) { + const addressData = encodeFunctionData({ + abi: l2ResolverABI, + functionName: "setAddr", + args: [namehash(baseName), addressId], + }); + const nameData = encodeFunctionData({ + abi: l2ResolverABI, + functionName: "setName", + args: [namehash(baseName), baseName], + }); + + const registerArgs = { + request: [ + baseName.replace(/\.base\.eth$/, ""), + addressId, + "31557600", + BaseNamesRegistrarControllerAddress, + [addressData, nameData], + true, + ], + }; + console.log(`Register contract method arguments constructed: `, registerArgs); + + return registerArgs; +} + +// Register a Basename for the given Wallet. +async function registerBaseName(wallet, registerArgs) { + try { + const contractInvocation = await wallet.invokeContract({ + contractAddress: BaseNamesRegistrarControllerAddress, + method: "register", + abi: registrarABI, + args: registerArgs, + amount: 0.002, + assetId: Coinbase.assets.Eth, + }); + + await contractInvocation.wait(); + + console.log(`Successfully registered Basename ${registerArgs[0]} for wallet: `, wallet); + } catch (error) { + console.error(`Error registering a Basename for ${wallet}: `, error); + } +} + +// Fetch a funded Wallet and load its Seed. +async function fetchWalletAndLoadSeed(walletId, seedFilePath) { + try { + const wallet = await Wallet.fetch(walletId); + await wallet.loadSeed(seedFilePath); + + console.log(`Successfully loaded funded wallet: `, wallet); + return wallet; + } catch (error) { + console.error( + `Error loading funded wallet ${walletId} from seed file ${seedFilePath}: `, + error, + ); + } +} + +async () => { + try { + const { BASE_NAME, WALLET_ID, SEED_FILE_PATH } = process.env; + + // Manage CDP API Key for Coinbase SDK. + // Configure location to CDP API Key. + Coinbase.configureFromJson({ + filePath: `${os.homedir()}/Downloads/cdp_api_key.json`, + }); + + // Fetch funded Wallet. + const wallet = await fetchWalletAndLoadSeed(WALLET_ID, SEED_FILE_PATH); + const defaultAddress = await wallet.getDefaultAddress(); + + // Register Basename. + const registerArgs = createRegisterContractMethodArgs(BASE_NAME, defaultAddress.getId()); + await registerBaseName(wallet, registerArgs); + } catch (error) { + console.error(`Error in registering a Basename for my wallet: `, error); + } +}; From 44abe9858c58664c799dade7bbf75bb23f14a1a6 Mon Sep 17 00:00:00 2001 From: John Peterson <98187317+John-peterson-coinbase@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:52:22 -0400 Subject: [PATCH 7/7] [hotfix] Fix L2 Resolver Address on Register Basenames Quickstart (#244) --- quickstart-template/register-basename.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/quickstart-template/register-basename.js b/quickstart-template/register-basename.js index 9f4e9017..e95b6c49 100644 --- a/quickstart-template/register-basename.js +++ b/quickstart-template/register-basename.js @@ -78,6 +78,9 @@ const registrarABI = [ // Basenames Registrar Controller Contract Address. const BaseNamesRegistrarControllerAddress = "0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5"; +// L2 Resolver Contract Address. +const L2ResolverAddress = "0xC6d566A56A1aFf6508b41f6c90ff131615583BCD"; + // Create register contract method arguments. function createRegisterContractMethodArgs(baseName, addressId) { const addressData = encodeFunctionData({ @@ -96,7 +99,7 @@ function createRegisterContractMethodArgs(baseName, addressId) { baseName.replace(/\.base\.eth$/, ""), addressId, "31557600", - BaseNamesRegistrarControllerAddress, + L2ResolverAddress, [addressData, nameData], true, ],