From fba42efe8fc0257264ec1e5461a1a0186341b4ff Mon Sep 17 00:00:00 2001 From: gmolki Date: Wed, 28 Aug 2024 12:31:47 +0200 Subject: [PATCH 01/10] feat: create base package --- package-lock.json | 108 +++++++++++++++++++++++- package.json | 9 +- packages/base/README.md | 9 ++ packages/base/__tests__/account.test.ts | 87 +++++++++++++++++++ packages/base/package.json | 38 +++++++++ packages/base/src/account.ts | 72 ++++++++++++++++ packages/base/src/index.ts | 1 + packages/client/README.md | 5 +- 8 files changed, 324 insertions(+), 5 deletions(-) create mode 100644 packages/base/README.md create mode 100644 packages/base/__tests__/account.test.ts create mode 100644 packages/base/package.json create mode 100644 packages/base/src/account.ts create mode 100644 packages/base/src/index.ts diff --git a/package-lock.json b/package-lock.json index 6d6b3f54..701bb7ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "packages/ethereum", "packages/ethereum-ledger", "packages/avalanche", + "packages/base", "packages/nuls2", "packages/cosmos", "packages/solana", @@ -207,6 +208,10 @@ "resolved": "packages/avalanche", "link": true }, + "node_modules/@aleph-sdk/base": { + "resolved": "packages/base", + "link": true + }, "node_modules/@aleph-sdk/client": { "resolved": "packages/client", "link": true @@ -29763,6 +29768,68 @@ "node": ">=16.0.0" } }, + "packages/base": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "eciesjs": "^0.4.6", + "sha.js": "^2.4.11" + }, + "devDependencies": { + "@types/sha.js": "^2.4.0" + }, + "peerDependencies": { + "@aleph-sdk/account": "^1.x.x", + "@aleph-sdk/core": "^1.0.4", + "@aleph-sdk/ethereum": "^1.x.x", + "@aleph-sdk/evm": "^1.0.4", + "avalanche": "^3.15.3", + "ethers": "^5.x.x" + } + }, + "packages/base/node_modules/@noble/ciphers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", + "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/base/node_modules/@noble/curves": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.5.0.tgz", + "integrity": "sha512-J5EKamIHnKPyClwVrzmaf5wSdQXgdHcPZIZLu3bwnbeCx8/7NPK5q2ZBWF+5FvYGByjiQQsJYX6jfgB2wDPn3A==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/base/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/base/node_modules/eciesjs": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.7.tgz", + "integrity": "sha512-4JQahOkBdDy27jjW4q3FJQigHlcwZXx28sCtBQkBamF2XUdcNXrInpgrr8h205MtVIS0CMHufyIKGVjtjxQ2ZA==", + "dependencies": { + "@noble/ciphers": "^0.5.3", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "packages/client": { "name": "@aleph-sdk/client", "version": "1.0.6", @@ -30030,7 +30097,7 @@ }, "packages/superfluid": { "name": "@aleph-sdk/superfluid", - "version": "1.0.6", + "version": "1.0.0", "license": "MIT", "devDependencies": { "@types/sha.js": "^2.4.0" @@ -30038,6 +30105,7 @@ "peerDependencies": { "@aleph-sdk/account": "^1.x.x", "@aleph-sdk/avalanche": "^1.x.x", + "@aleph-sdk/base": "^1.x.x", "@aleph-sdk/core": "^1.x.x", "@aleph-sdk/evm": "^1.x.x", "@superfluid-finance/sdk-core": "^0.6.12", @@ -30255,6 +30323,44 @@ } } }, + "@aleph-sdk/base": { + "version": "file:packages/base", + "requires": { + "@types/sha.js": "^2.4.0", + "eciesjs": "^0.4.6", + "sha.js": "^2.4.11" + }, + "dependencies": { + "@noble/ciphers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", + "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==" + }, + "@noble/curves": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.5.0.tgz", + "integrity": "sha512-J5EKamIHnKPyClwVrzmaf5wSdQXgdHcPZIZLu3bwnbeCx8/7NPK5q2ZBWF+5FvYGByjiQQsJYX6jfgB2wDPn3A==", + "requires": { + "@noble/hashes": "1.4.0" + } + }, + "@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==" + }, + "eciesjs": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.7.tgz", + "integrity": "sha512-4JQahOkBdDy27jjW4q3FJQigHlcwZXx28sCtBQkBamF2XUdcNXrInpgrr8h205MtVIS0CMHufyIKGVjtjxQ2ZA==", + "requires": { + "@noble/ciphers": "^0.5.3", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0" + } + } + } + }, "@aleph-sdk/client": { "version": "file:packages/client", "requires": { diff --git a/package.json b/package.json index dff720b3..f19f483d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,9 @@ "messages", "ethereum", "solana", + "avalanche", + "avax", + "base", "defi", "dao", "nft" @@ -32,7 +35,8 @@ "Roman Gascoin ", "Mike Hukiewitz ", "Kelian Christophe ", - "Angel Manzano " + "Angel Manzano ", + "Gerard Molina " ], "license": "MIT", "engines": { @@ -73,6 +77,7 @@ "packages/ethereum", "packages/ethereum-ledger", "packages/avalanche", + "packages/base", "packages/nuls2", "packages/cosmos", "packages/solana", @@ -83,4 +88,4 @@ "packages/client", "packages/dns" ] -} \ No newline at end of file +} diff --git a/packages/base/README.md b/packages/base/README.md new file mode 100644 index 00000000..31a53998 --- /dev/null +++ b/packages/base/README.md @@ -0,0 +1,9 @@ +# @aleph-sdk/base + +This package provides an implementation for Base blockchain accounts within the Aleph.im ecosystem, enabling Base account management and message signing functionalities. + +See [@aleph-sdk/client](https://npmjs.com/package/@aleph-sdk/client) or the [offical docs](https://docs.aleph.im) as the entrypoint for developing with aleph.im. + +## Features + +As Base Account extends from Ethereum account, see [@aleph-sdk/ethereum](https://www.npmjs.com/package/@aleph-sdk/ethereum) diff --git a/packages/base/__tests__/account.test.ts b/packages/base/__tests__/account.test.ts new file mode 100644 index 00000000..4158a58e --- /dev/null +++ b/packages/base/__tests__/account.test.ts @@ -0,0 +1,87 @@ +import * as bip39 from 'bip39' +import { ethers } from 'ethers' + +import * as base from '../src' +import { PostMessageBuilder, prepareAlephMessage, ItemType } from '../../message/src' +import { EthereumMockProvider } from '@aleph-sdk/evm' +import { EphAccount } from '@aleph-sdk/account' + +async function createEphemeralEth(): Promise { + const mnemonic = bip39.generateMnemonic() + const { address, publicKey, privateKey } = ethers.Wallet.fromMnemonic(mnemonic) + + return { + address, + publicKey, + privateKey: privateKey.substring(2), + mnemonic, + } +} + +describe('Ethereum accounts', () => { + let ephemeralAccount: EphAccount + + beforeAll(async () => { + ephemeralAccount = await createEphemeralEth() + }) + + it('should import a base account using a mnemonic', () => { + const { account, mnemonic } = base.newAccount() + const accountFromMnemonic = base.importAccountFromMnemonic(mnemonic) + + expect(account.address).toStrictEqual(accountFromMnemonic.address) + }) + + it('should import a base account using a private key', () => { + const mnemonic = bip39.generateMnemonic() + const wallet = ethers.Wallet.fromMnemonic(mnemonic) + const accountFromPrivate = base.importAccountFromPrivateKey(wallet.privateKey) + + expect(wallet.address).toStrictEqual(accountFromPrivate.address) + }) + + it('should import a base account using a provider', async () => { + const { address, privateKey } = ephemeralAccount + if (!privateKey) throw Error('Can not retrieve privateKey inside ephemeralAccount.json') + + const provider = new EthereumMockProvider({ + address, + privateKey, + networkVersion: 31, + }) + + const accountFromProvider = await base.getAccountFromProvider(provider) + const accountFromPrivate = base.importAccountFromPrivateKey(privateKey) + + expect(accountFromProvider.address).toStrictEqual(accountFromPrivate.address) + }) + + it('should get the same signed message for each account', async () => { + const { address, privateKey } = ephemeralAccount + if (!privateKey) throw Error('Can not retrieve privateKey inside ephemeralAccount.json') + + const provider = new EthereumMockProvider({ + address, + privateKey, + networkVersion: 31, + }) + const { account, mnemonic } = base.newAccount() + const accountFromProvider = await base.getAccountFromProvider(provider) + const accountFromPrivate = await base.importAccountFromMnemonic(mnemonic) + + const builtMessage = PostMessageBuilder({ + account, + channel: 'TEST', + storageEngine: ItemType.inline, + timestamp: Date.now() / 1000, + content: { address: account.address, time: 15, type: '' }, + }) + + const hashedMessage = await prepareAlephMessage({ + message: builtMessage, + }) + + expect(account.sign(hashedMessage)).toStrictEqual(accountFromPrivate.sign(hashedMessage)) + expect(account.sign(hashedMessage)).toStrictEqual(accountFromProvider.sign(hashedMessage)) + }) +}) diff --git a/packages/base/package.json b/packages/base/package.json new file mode 100644 index 00000000..81a7ef7d --- /dev/null +++ b/packages/base/package.json @@ -0,0 +1,38 @@ +{ + "name": "@aleph-sdk/base", + "version": "1.0.0", + "description": "Base accounts for signing messages on aleph.im", + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@types/sha.js": "^2.4.0" + }, + "dependencies": { + "eciesjs": "^0.4.6", + "sha.js": "^2.4.11" + }, + "peerDependencies": { + "@aleph-sdk/account": "^1.x.x", + "@aleph-sdk/core": "^1.0.4", + "@aleph-sdk/evm": "^1.0.4", + "@aleph-sdk/ethereum": "^1.x.x", + "avalanche": "^3.15.3", + "ethers": "^5.x.x" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "rollup": "rollup -c ../../rollup.config.js", + "build": "npm run rollup" + }, + "author": "", + "homepage": "https://aleph.im", + "bugs": "https://github.com/aleph-im/aleph-sdk-ts/issues", + "license": "MIT" +} diff --git a/packages/base/src/account.ts b/packages/base/src/account.ts new file mode 100644 index 00000000..185f36e1 --- /dev/null +++ b/packages/base/src/account.ts @@ -0,0 +1,72 @@ +import * as bip39 from 'bip39' +import { Blockchain } from '@aleph-sdk/core' +import { ETHAccount } from '@aleph-sdk/ethereum' +import { ethers } from 'ethers' +import { ChangeRpcParam, JsonRPCWallet, RpcId } from '@aleph-sdk/evm' + +/** + * BaseAccount implements the Account class for the Base protocol. + * It is used to represent a Base account when publishing a message on the Aleph network. + */ +export class BaseAccount extends ETHAccount { + override getChain(): Blockchain { + return Blockchain.BASE + } +} + +/** + * Imports an Base account given a mnemonic and the 'ethers' package. + * + * It creates an Base wallet containing information about the account, extracted in the BaseAccount constructor. + * + * @param mnemonic The mnemonic of the account to import. + * @param derivationPath The derivation path used to retrieve the list of accounts attached to the given mnemonic. + */ +export function importAccountFromMnemonic(mnemonic: string, derivationPath = "m/44'/60'/0'/0/0"): BaseAccount { + const wallet = ethers.Wallet.fromMnemonic(mnemonic, derivationPath) + + return new BaseAccount(wallet, wallet.address, wallet.publicKey) +} + +/** + * Imports an Base account given a private key and the 'ethers' package. + * + * It creates an Base wallet containing information about the account, extracted in the BaseAccount constructor. + * + * @param privateKey The private key of the account to import. + */ +export function importAccountFromPrivateKey(privateKey: string): BaseAccount { + const wallet = new ethers.Wallet(privateKey) + + return new BaseAccount(wallet, wallet.address, wallet.publicKey) +} + +/** + * Creates a new Base account using a generated mnemonic following BIP 39 standard. + * + * @param derivationPath + */ +export function newAccount(derivationPath = "m/44'/60'/0'/0/0"): { account: BaseAccount; mnemonic: string } { + const mnemonic = bip39.generateMnemonic() + + return { account: importAccountFromMnemonic(mnemonic, derivationPath), mnemonic: mnemonic } +} + +/** + * Get an account from a Web3 provider (ex: Metamask) + * + * @param {ethers.providers.ExternalProvider} provider + * @param requestedRpc Use this params to change the RPC endpoint; + */ +export async function getAccountFromProvider( + provider: ethers.providers.ExternalProvider, + requestedRpc: ChangeRpcParam = RpcId.BASE, +): Promise { + const ETHprovider = new ethers.providers.Web3Provider(provider) + const jrw = new JsonRPCWallet(ETHprovider) + await jrw.changeNetwork(requestedRpc) + await jrw.connect() + + if (jrw.address) return new BaseAccount(jrw, jrw.address) + throw new Error('Insufficient permissions') +} diff --git a/packages/base/src/index.ts b/packages/base/src/index.ts new file mode 100644 index 00000000..8a695b2e --- /dev/null +++ b/packages/base/src/index.ts @@ -0,0 +1 @@ +export * from './account' diff --git a/packages/client/README.md b/packages/client/README.md index 2c26f283..4f91c317 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -6,6 +6,7 @@ This iteration of the SDK is a complete rewrite of the original [aleph-ts-sdk](h Each chain has its own package: - [@aleph-sdk/avalanche](https://npmjs.com/package/@aleph-sdk/avalanche) +- [@aleph-sdk/base](https://npmjs.com/package/@aleph-sdk/base) - [@aleph-sdk/cosmos](https://npmjs.com/package/@aleph-sdk/cosmos) - [@aleph-sdk/ethereum](https://npmjs.com/package/@aleph-sdk/ethereum) - [@aleph-sdk/ethereum-ledger](https://npmjs.com/package/@aleph-sdk/ethereum-ledger) @@ -20,7 +21,7 @@ This particular client package is a wrapper arount [@aleph-sdk/message](https:// Additional domain and DNS-related functions can be found in [@aleph-sdk/dns](https://npmjs.com/package/@aleph-sdk/dns) or in the [official docs](https://docs.aleph.im/computing/custom_domain/setup/). -Establishing [ALEPH streams to create VPS instances](https://docs.aleph.im/libraries/typescript-sdk/instances/) using Superfluid on Avalanche can be done with [@aleph-sdk/superfluid](https://npmjs.com/package/@aleph-sdk/superfluid). +Establishing [ALEPH streams to create VPS instances](https://docs.aleph.im/libraries/typescript-sdk/instances/) using Superfluid with a compatible chain can be done with [@aleph-sdk/superfluid](https://npmjs.com/package/@aleph-sdk/superfluid). ## Installation @@ -38,4 +39,4 @@ npm install @aleph-sdk/ethereum ## Usage -See the official [Aleph Docs](https://docs.aleph.im/) for more information on how to use the SDK. \ No newline at end of file +See the official [Aleph Docs](https://docs.aleph.im/) for more information on how to use the SDK. From 36b5e896fef3d36568ccb3cfc5890b0b228733d5 Mon Sep 17 00:00:00 2001 From: gmolki Date: Wed, 28 Aug 2024 12:33:16 +0200 Subject: [PATCH 02/10] [CORE] feat: add Base Blockchain --- packages/core/src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index b0e6676a..254012f5 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -8,6 +8,7 @@ export enum Blockchain { CSDK = 'CSDK', NEO = 'NEO', TEZOS = 'TEZOS', + BASE = 'BASE', } /** From a730e0b348b77b80d4ccd1f72804f3a122b5d381 Mon Sep 17 00:00:00 2001 From: gmolki Date: Wed, 28 Aug 2024 12:33:51 +0200 Subject: [PATCH 03/10] [CORE] v1.0.4 --- package-lock.json | 3 ++- packages/core/package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 701bb7ba..29dd7ed9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29769,6 +29769,7 @@ } }, "packages/base": { + "name": "@aleph-sdk/base", "version": "1.0.0", "license": "MIT", "dependencies": { @@ -29845,7 +29846,7 @@ }, "packages/core": { "name": "@aleph-sdk/core", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT" }, "packages/cosmos": { diff --git a/packages/core/package.json b/packages/core/package.json index 3298273e..3b251a23 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@aleph-sdk/core", - "version": "1.0.3", + "version": "1.0.4", "description": "Shared utilities and constants for @aleph-sdk", "main": "dist/index.cjs", "module": "dist/index.mjs", From b8fb8d060de4bb97c781a3fdb54a5b1936f4357b Mon Sep 17 00:00:00 2001 From: gmolki Date: Wed, 28 Aug 2024 12:35:43 +0200 Subject: [PATCH 04/10] [EVM] feat: Add Base + ETH Sepolia chains data --- packages/evm/src/provider/rpc.ts | 77 +++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/packages/evm/src/provider/rpc.ts b/packages/evm/src/provider/rpc.ts index eb129710..2f188bb1 100644 --- a/packages/evm/src/provider/rpc.ts +++ b/packages/evm/src/provider/rpc.ts @@ -1,16 +1,19 @@ import { ethers } from 'ethers' import { BaseProviderWallet } from '@aleph-sdk/account' -const RPC_WARNING = `DEPRECATION WARNING: +const RPC_WARNING = `DEPRECATION WARNING: Encryption/Decryption features may become obsolete, for more information: https://github.com/aleph-im/aleph-sdk-ts/issues/37` export enum RpcId { ETH, ETH_FLASHBOTS, + ETH_SEPOLIA, POLYGON, BSC, AVAX, AVAX_TESTNET, + BASE, + BASE_TESTNET, } export type RpcType = { @@ -25,6 +28,11 @@ export type RpcType = { blockExplorerUrls: string[] } +export type ChainMetadataType = RpcType & { + tokenAddress?: string + superTokenAddress?: string +} + export type ChangeRpcParam = RpcType | RpcId export function decToHex(dec: number): string { @@ -80,6 +88,17 @@ export const ChainData: { [key: number]: RpcType } = { }, blockExplorerUrls: ['https://etherscan.io'], }, + [RpcId.ETH_SEPOLIA]: { + chainId: decToHex(11155111), + rpcUrls: ['https://eth-sepolia.public.blastapi.io/'], + chainName: 'Ethereum Sepolia (Testnet)', + nativeCurrency: { + name: 'ETH', + symbol: 'ETH', + decimals: 18, + }, + blockExplorerUrls: ['https://sepolia.etherscan.io'], + }, [RpcId.POLYGON]: { chainId: decToHex(137), rpcUrls: ['https://polygon-rpc.com/'], @@ -102,6 +121,56 @@ export const ChainData: { [key: number]: RpcType } = { }, blockExplorerUrls: ['https://bscscan.com'], }, + [RpcId.BASE]: { + chainId: decToHex(8453), + rpcUrls: ['https://mainnet.base.org'], + chainName: 'Base Mainnet', + nativeCurrency: { + name: 'ETH', + symbol: 'ETH', + decimals: 18, + }, + blockExplorerUrls: ['https://basescan.org'], + }, + [RpcId.BASE_TESTNET]: { + chainId: decToHex(84532), + rpcUrls: ['https://sepolia.base.org'], + chainName: 'Base Sepolia (Testnet)', + nativeCurrency: { + name: 'ETH', + symbol: 'ETH', + decimals: 18, + }, + blockExplorerUrls: [' https://sepolia-explorer.base.org'], + }, +} + +export const ChainMetadata: { [key: number]: ChainMetadataType } = { + ...ChainData, + [RpcId.AVAX]: { + ...ChainData[RpcId.AVAX], + tokenAddress: '0xc0Fbc4967259786C743361a5885ef49380473dCF', + superTokenAddress: '0xc0Fbc4967259786C743361a5885ef49380473dCF', + }, + [RpcId.AVAX_TESTNET]: { + ...ChainData[RpcId.AVAX_TESTNET], + tokenAddress: '0x1290248E01ED2F9f863A9752A8aAD396ef3a1B00', + superTokenAddress: '0x1290248E01ED2F9f863A9752A8aAD396ef3a1B00', + }, + [RpcId.ETH_SEPOLIA]: { + ...ChainData[RpcId.ETH_SEPOLIA], + tokenAddress: '0xc4bf5cbdabe595361438f8c6a187bdc330539c60', + superTokenAddress: '0x22064a21fee226d8ffb8818e7627d5ff6d0fc33a', + }, + [RpcId.BASE]: { + ...ChainData[RpcId.BASE], + tokenAddress: '0xc0Fbc4967259786C743361a5885ef49380473dCF', + superTokenAddress: '0xc0Fbc4967259786C743361a5885ef49380473dCF', + }, +} + +export function findChainMetadataByChainId(chainId: number): ChainMetadataType | undefined { + return Object.values(ChainMetadata).find((chain) => hexToDec(chain.chainId) === chainId) } /** @@ -149,11 +218,9 @@ export class JsonRPCWallet extends BaseProviderWallet { public async changeNetwork(chainOrRpc: RpcType | RpcId = RpcId.ETH): Promise { if (typeof chainOrRpc === 'number') { - if (chainOrRpc === RpcId.ETH) { - await this.provider.send('wallet_switchEthereumChain', [{ chainId: '0x1' }]) - } else await this.provider.send('wallet_addEthereumChain', [ChainData[chainOrRpc]]) + await this.provider.send('wallet_switchEthereumChain', [{ chainId: ChainData[chainOrRpc].chainId }]) } else { - await this.provider.send('wallet_addEthereumChain', [chainOrRpc]) + await this.provider.send('wallet_switchEthereumChain', [{ chainId: chainOrRpc }]) } } From 7adca8d49ffd85dfba89e64c3d4bdb149df77250 Mon Sep 17 00:00:00 2001 From: gmolki Date: Wed, 28 Aug 2024 12:36:08 +0200 Subject: [PATCH 05/10] [EVM] v1.0.4 --- package-lock.json | 2 +- packages/evm/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29dd7ed9..30e3f71c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29961,7 +29961,7 @@ }, "packages/evm": { "name": "@aleph-sdk/evm", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "dependencies": { "@metamask/eth-sig-util": "^7.0.1", diff --git a/packages/evm/package.json b/packages/evm/package.json index 991aaf4e..e6ca9ea7 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -1,6 +1,6 @@ { "name": "@aleph-sdk/evm", - "version": "1.0.3", + "version": "1.0.4", "description": "Common functions for EVM-based accounts interacting with aleph.im.", "main": "dist/index.cjs", "module": "dist/index.mjs", From 66bfd7690ffc9df2d6baa2e2850e368f98a2358d Mon Sep 17 00:00:00 2001 From: gmolki Date: Wed, 28 Aug 2024 19:04:49 +0200 Subject: [PATCH 06/10] feat: generalize SuperfluidAccount to handle EVM Accounts --- packages/superfluid/src/account.ts | 317 +++++++++++---------------- packages/superfluid/src/constants.ts | 4 - packages/superfluid/src/index.ts | 1 - 3 files changed, 127 insertions(+), 195 deletions(-) delete mode 100644 packages/superfluid/src/constants.ts diff --git a/packages/superfluid/src/account.ts b/packages/superfluid/src/account.ts index 21ef1704..7114c7b5 100644 --- a/packages/superfluid/src/account.ts +++ b/packages/superfluid/src/account.ts @@ -1,56 +1,76 @@ import { Framework, SuperToken } from '@superfluid-finance/sdk-core' -import { AvalancheAccount } from '@aleph-sdk/avalanche' -import { ChainData, ChangeRpcParam, decToHex, hexToDec, JsonRPCWallet, RpcId } from '@aleph-sdk/evm' -import { BigNumber, ethers, providers } from 'ethers' +import { AvalancheAccount, getAccountFromProvider as getAvalancheAccountFromProvider } from '@aleph-sdk/avalanche' +import { BaseAccount, getAccountFromProvider as getBaseAccountFromProvider } from '@aleph-sdk/base' +import { getAccountFromProvider as getEthereumAccountFromProvider } from '@aleph-sdk/ethereum' import { - ALEPH_SUPERFLUID_FUJI_TESTNET, - ALEPH_SUPERFLUID_MAINNET, - SUPERFLUID_FUJI_TESTNET_SUBGRAPH_URL, - SUPERFLUID_MAINNET_SUBGRAPH_URL, -} from './constants' + ChainData, + ChangeRpcParam, + EVMAccount, + findChainMetadataByChainId, + hexToDec, + JsonRPCWallet, + RpcId, +} from '@aleph-sdk/evm' +import { BigNumber, ethers, providers } from 'ethers' import { Decimal } from 'decimal.js' -import { BaseProviderWallet } from '@aleph-sdk/account' +import { Account, SignableMessage } from '@aleph-sdk/account' +import { Blockchain } from '@aleph-sdk/core' /** * SuperfluidAccount implements the Account class for the Superfluid protocol. * It is used to represent a Superfluid account when publishing a message on the Aleph network. */ -export class SuperfluidAccount extends AvalancheAccount { +export class SuperfluidAccount extends EVMAccount { public override wallet: JsonRPCWallet + private account: EVMAccount private framework?: Framework private alephx?: SuperToken - constructor(wallet: BaseProviderWallet | ethers.providers.JsonRpcProvider, address: string, publicKey?: string) { - super(wallet, address, publicKey) - if (wallet instanceof JsonRPCWallet) this.wallet = wallet - else if (wallet instanceof ethers.providers.JsonRpcProvider) this.wallet = new JsonRPCWallet(wallet) + constructor(account: EVMAccount) { + super(account.address, account.publicKey) + this.account = account + + if (account.wallet instanceof JsonRPCWallet) this.wallet = account.wallet + else if (account.wallet instanceof ethers.providers.JsonRpcProvider) this.wallet = new JsonRPCWallet(account.wallet) else throw new Error('Unsupported wallet type') } public async init(): Promise { if (!this.wallet) throw Error('PublicKey Error: No providers are set up') await this.wallet.connect() + if (!this.framework) { this.framework = await Framework.create({ chainId: await this.wallet.getCurrentChainId(), provider: this.wallet.provider, }) } - if (!this.alephx) { - if (ChainData[RpcId.AVAX_TESTNET].chainId === decToHex(await this.getChainId())) { - this.alephx = await this.framework.loadSuperToken(ALEPH_SUPERFLUID_FUJI_TESTNET) - } else if (ChainData[RpcId.AVAX].chainId === decToHex(await this.getChainId())) { - this.alephx = await this.framework.loadSuperToken(ALEPH_SUPERFLUID_MAINNET) - } else { - throw new Error(`ChainID ${await this.getChainId()} not supported`) - } - } + + const currentChainId = await this.getChainId() + const superTokenAddress = findChainMetadataByChainId(currentChainId)?.superTokenAddress + + if (!superTokenAddress) throw new Error(`ChainID ${currentChainId} not supported`) + + this.alephx = await this.framework.loadSuperToken(superTokenAddress) + } + + public override async askPubKey() { + return this.account.askPubKey() + } + + public override getChain() { + return this.account.getChain() + } + + public override async sign(message: SignableMessage) { + return this.account.sign(message) } private alephToWei(alephAmount: Decimal | number): ethers.BigNumber { // @note: Need to pre-multiply the number as Decimal in order to correctly parse as BigNumber const alephAmountBN = new Decimal(alephAmount).mul(10 ** 18) + return ethers.BigNumber.from(alephAmountBN.toString()) } @@ -72,7 +92,12 @@ export class SuperfluidAccount extends AvalancheAccount { public async getALEPHBalance(): Promise { if (!this.wallet) throw Error('PublicKey Error: No providers are set up') if (!this.alephx) throw new Error('SuperfluidAccount not initialized') - const balance = await this.alephx.balanceOf({ account: this.address, providerOrSigner: this.wallet.provider }) + + const balance = await this.alephx.balanceOf({ + account: this.address, + providerOrSigner: this.wallet.provider, + }) + return this.weiToAleph(balance) } @@ -83,117 +108,16 @@ export class SuperfluidAccount extends AvalancheAccount { public async getALEPHFlow(receiver: string): Promise { if (!this.wallet) throw Error('PublicKey Error: No providers are set up') if (!this.alephx) throw new Error('SuperfluidAccount not initialized') + const flow = await this.alephx.getFlow({ sender: this.address, receiver, providerOrSigner: this.wallet.provider, }) - if (!flow) { - return new Decimal(0) - } else { - return this.flowRateToAlephPerHour(flow.flowRate) - } - } - /** - * Get the net ALEPH flow rate of the account in ALEPH per hour. - */ - public async getNetALEPHFlow(): Promise { - if (!this.wallet) throw Error('PublicKey Error: No providers are set up') - if (!this.alephx) throw new Error('SuperfluidAccount not initialized') - const flow = await this.alephx.getNetFlow({ - account: this.address, - providerOrSigner: this.wallet.provider, - }) - return this.flowRateToAlephPerHour(flow) - } + if (!flow) return new Decimal(0) - public async getAllALEPHOutflows(): Promise> { - if (!this.wallet) throw Error('PublicKey Error: No providers are set up') - if (!this.framework || !this.alephx) throw new Error('SuperfluidAccount not initialized') - // make a graphql query to Superfluid Subgraph - const query = ` - query { - accounts(where: {outflows_: {sender: ${this.address}}}) { - id - outflows(where: {currentFlowRate_not: "0"}) { - sender { - id - } - receiver { - id - } - createdAtTimestamp - currentFlowRate - } - } - } - ` - const data = await this.querySubgraph(query) - const accounts = data.data.accounts - const outflows: Record = {} - for (const account of accounts) { - for (const outflow of account.outflows) { - const flowRate = this.flowRateToAlephPerHour(outflow.currentFlowRate) - if (outflow.sender.id === this.address) { - outflows[outflow.sender.id] = flowRate - } - } - } - return outflows - } - - private async querySubgraph(query: string) { - const response = await fetch(await this.getSubgraphUrl(), { - method: 'POST', - body: JSON.stringify({ query }), - }) - return await response.json() - } - - public async getAllALEPHInflows(): Promise> { - if (!this.wallet) throw Error('PublicKey Error: No providers are set up') - if (!this.framework || !this.alephx) throw new Error('SuperfluidAccount not initialized') - // make a graphql query to Superfluid Subgraph - const query = ` - query { - accounts(where: {inflows_: {receiver: ${this.address}}}) { - id - inflows(where: {currentFlowRate_not: "0"}) { - sender { - id - } - receiver { - id - } - createdAtTimestamp - currentFlowRate - } - } - } - ` - const data = await this.querySubgraph(query) - const accounts = data.data.accounts - const inflows: Record = {} - for (const account of accounts) { - for (const inflow of account.inflows) { - const flowRate = this.flowRateToAlephPerHour(inflow.currentFlowRate) - if (inflow.receiver.id === this.address) { - inflows[inflow.sender.id] = flowRate - } - } - } - return inflows - } - - private async getSubgraphUrl() { - if (ChainData[RpcId.AVAX_TESTNET].chainId === decToHex(await this.getChainId())) { - return SUPERFLUID_FUJI_TESTNET_SUBGRAPH_URL - } else if (ChainData[RpcId.AVAX].chainId === decToHex(await this.getChainId())) { - return SUPERFLUID_MAINNET_SUBGRAPH_URL - } else { - throw new Error(`ChainID ${await this.getChainId()} not supported`) - } + return this.flowRateToAlephPerHour(flow.flowRate) } /** @@ -204,35 +128,26 @@ export class SuperfluidAccount extends AvalancheAccount { public async increaseALEPHFlow(receiver: string, alephPerHour: Decimal | number): Promise { if (!this.wallet) throw Error('PublicKey Error: No providers are set up') if (!this.alephx) throw new Error('SuperfluidAccount is not initialized') - const flow = await this.alephx.getFlow({ - sender: this.address, - receiver, - providerOrSigner: this.wallet.provider, - }) + // check wrapped balance, if none throw const balance = ethers.BigNumber.from( await this.alephx.balanceOf({ account: this.address, providerOrSigner: this.wallet.provider }), ) - if (balance.lt(this.alephToWei(alephPerHour))) { - throw new Error('Not enough ALEPH to increase flow') - } + if (balance.lt(this.alephToWei(alephPerHour))) throw new Error('Not enough ALEPH to increase flow') + const signer = this.wallet.provider.getSigner() - if (!flow || BigNumber.from(flow.flowRate).eq(0)) { - const op = this.alephx.createFlow({ - sender: this.address, - receiver, - flowRate: this.alephPerHourToFlowRate(alephPerHour).toString(), - }) - await op.exec(signer) - } else { - const newFlowRate = ethers.BigNumber.from(flow.flowRate.toString()).add(this.alephPerHourToFlowRate(alephPerHour)) - const op = this.alephx.updateFlow({ - sender: this.address, - receiver, - flowRate: newFlowRate.toString(), - }) - await op.exec(signer) - } + const sender = this.address + const flow = await this.getFlow(receiver) + + const newFlowRate = flow + ? ethers.BigNumber.from(flow.flowRate.toString()).add(this.alephPerHourToFlowRate(alephPerHour)) + : this.alephPerHourToFlowRate(alephPerHour) + + const operation = flow + ? this.alephx.updateFlow({ sender, receiver, flowRate: newFlowRate.toString() }) + : this.alephx.createFlow({ sender, receiver, flowRate: newFlowRate.toString() }) + + await operation.exec(signer) } /** @@ -243,43 +158,54 @@ export class SuperfluidAccount extends AvalancheAccount { public async decreaseALEPHFlow(receiver: string, alephPerHour: Decimal | number): Promise { if (!this.wallet) throw Error('PublicKey Error: No providers are set up') if (!this.alephx) throw new Error('SuperfluidAccount not initialized') - const flow = await this.alephx.getFlow({ + + const flow = await this.getFlow(receiver) + if (!flow) return + + const sender = this.address + const signer = this.wallet.provider.getSigner() + const newFlowRate = ethers.BigNumber.from(flow.flowRate.toString()).sub(this.alephPerHourToFlowRate(alephPerHour)) + + const operation = newFlowRate.lte(0) + ? this.alephx.deleteFlow({ sender, receiver }) + : this.alephx.updateFlow({ sender, receiver, flowRate: newFlowRate.toString() }) + + await operation.exec(signer) + } + + private async getFlow(receiver: string) { + if (!this.wallet) throw Error('PublicKey Error: No providers are set up') + if (!this.alephx) throw new Error('SuperfluidAccount not initialized') + + const fetchedFlow = await this.alephx.getFlow({ sender: this.address, receiver, providerOrSigner: this.wallet.provider, }) - if (!flow || BigNumber.from(flow.flowRate).eq(0)) return - const newFlowRate = ethers.BigNumber.from(flow.flowRate.toString()).sub(this.alephPerHourToFlowRate(alephPerHour)) - const signer = this.wallet.provider.getSigner() - if (newFlowRate.lte(0)) { - const op = this.alephx.deleteFlow({ - sender: this.address, - receiver, - }) - await op.exec(signer) - } else { - const op = this.alephx.updateFlow({ - sender: this.address, - receiver, - flowRate: newFlowRate.toString(), - }) - await op.exec(signer) - } + + return !fetchedFlow || BigNumber.from(fetchedFlow.flowRate).eq(0) ? null : fetchedFlow } } -export function createFromAvalancheAccount(account: AvalancheAccount, rpc?: string): SuperfluidAccount { - if (account.wallet) { - return new SuperfluidAccount(account.wallet, account.address, account.publicKey) - } - throw new Error('Wallet is required') - // @todo: implement for node.js - if (!rpc) throw new Error('RPC or Provider is required') - if (!account.keyPair) throw new Error('KeyPair is required') - - const rpcProvider = new ethers.providers.JsonRpcProvider(rpc) - const provider = new JsonRPCWallet(rpcProvider) - return new SuperfluidAccount(provider, account.address, account.publicKey) +export function isBlockchainSupported(blockchain: Blockchain | undefined): boolean { + if (!blockchain) return false + + return [Blockchain.AVAX, Blockchain.BASE].includes(blockchain) +} + +export function isAccountSupported(account: Account | undefined): boolean { + if (!account) return false + + return account instanceof AvalancheAccount || account instanceof BaseAccount +} + +export async function createFromEVMAccount(account: EVMAccount): Promise { + if (!account.wallet) throw new Error('Wallet error: Could not connect to wallet') + + const superfluidAccount = new SuperfluidAccount(account) + await superfluidAccount.init() + + return superfluidAccount } export async function getAccountFromProvider( @@ -292,12 +218,23 @@ export async function getAccountFromProvider( } else { networkInfo = hexToDec(requestedRpc.chainId) } - const web3Provider = new providers.Web3Provider(provider, networkInfo) - const wallet = new JsonRPCWallet(web3Provider) - if (!wallet.address) await wallet.connect() - if (!wallet.address) throw new Error('PublicKey Error: No providers are set up') - const account = new SuperfluidAccount(wallet, wallet.address) - await account.changeNetwork(requestedRpc) - await account.init() - return account + + let account: EVMAccount + if ([hexToDec(ChainData[RpcId.AVAX].chainId), hexToDec(ChainData[RpcId.AVAX_TESTNET].chainId)].includes(networkInfo)) + account = await getAvalancheAccountFromProvider(provider) + else if ( + [hexToDec(ChainData[RpcId.BASE].chainId), hexToDec(ChainData[RpcId.BASE_TESTNET].chainId)].includes(networkInfo) + ) + account = await getBaseAccountFromProvider(provider) + else if ( + [ + hexToDec(ChainData[RpcId.ETH].chainId), + hexToDec(ChainData[RpcId.ETH_FLASHBOTS].chainId), + hexToDec(ChainData[RpcId.ETH_SEPOLIA].chainId), + ].includes(networkInfo) + ) + account = await getEthereumAccountFromProvider(provider) + else throw new Error(`Unsupported Chain: ${requestedRpc}`) + + return createFromEVMAccount(account) } diff --git a/packages/superfluid/src/constants.ts b/packages/superfluid/src/constants.ts deleted file mode 100644 index bdb6c217..00000000 --- a/packages/superfluid/src/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const ALEPH_SUPERFLUID_FUJI_TESTNET = '0x1290248E01ED2F9f863A9752A8aAD396ef3a1B00' -export const ALEPH_SUPERFLUID_MAINNET = '0xc0Fbc4967259786C743361a5885ef49380473dCF' -export const SUPERFLUID_FUJI_TESTNET_SUBGRAPH_URL = 'https://avalanche-fuji.subgraph.x.superfluid.dev/' -export const SUPERFLUID_MAINNET_SUBGRAPH_URL = 'https://avalanche-c.subgraph.x.superfluid.dev/' diff --git a/packages/superfluid/src/index.ts b/packages/superfluid/src/index.ts index d06cadb8..8a695b2e 100644 --- a/packages/superfluid/src/index.ts +++ b/packages/superfluid/src/index.ts @@ -1,2 +1 @@ export * from './account' -export * from './constants' From 75f621cd81d684f92ad3f551dfeb919fc0abf8e8 Mon Sep 17 00:00:00 2001 From: gmolki Date: Wed, 28 Aug 2024 19:05:01 +0200 Subject: [PATCH 07/10] [SUPERFLUID] v1.1.0 --- package-lock.json | 6 +++--- packages/superfluid/package.json | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30e3f71c..a2026de2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30098,7 +30098,7 @@ }, "packages/superfluid": { "name": "@aleph-sdk/superfluid", - "version": "1.0.0", + "version": "1.1.0", "license": "MIT", "devDependencies": { "@types/sha.js": "^2.4.0" @@ -30107,8 +30107,8 @@ "@aleph-sdk/account": "^1.x.x", "@aleph-sdk/avalanche": "^1.x.x", "@aleph-sdk/base": "^1.x.x", - "@aleph-sdk/core": "^1.x.x", - "@aleph-sdk/evm": "^1.x.x", + "@aleph-sdk/core": "^1.0.4", + "@aleph-sdk/evm": "^1.0.4", "@superfluid-finance/sdk-core": "^0.6.12", "decimal.js": "^10.4.3" } diff --git a/packages/superfluid/package.json b/packages/superfluid/package.json index 82c93e8b..c04dcaa6 100644 --- a/packages/superfluid/package.json +++ b/packages/superfluid/package.json @@ -1,6 +1,6 @@ { "name": "@aleph-sdk/superfluid", - "version": "1.0.6", + "version": "1.1.0", "description": "Aleph.im account wrapper for interacting with the Superfluid Avalanche Contract", "main": "dist/index.cjs", "module": "dist/index.mjs", @@ -17,8 +17,9 @@ "peerDependencies": { "@aleph-sdk/account": "^1.x.x", "@aleph-sdk/avalanche": "^1.x.x", - "@aleph-sdk/core": "^1.x.x", - "@aleph-sdk/evm": "^1.x.x", + "@aleph-sdk/base": "^1.x.x", + "@aleph-sdk/core": "^1.0.4", + "@aleph-sdk/evm": "^1.0.4", "@superfluid-finance/sdk-core": "^0.6.12", "decimal.js": "^10.4.3" }, From bcbbbfba1269aed405e2c0dc1e053de4b9b6ca96 Mon Sep 17 00:00:00 2001 From: gmolki Date: Sun, 1 Sep 2024 19:52:13 +0200 Subject: [PATCH 08/10] [EVM] feat: add chain when not listed on connect --- packages/evm/src/provider/rpc.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/evm/src/provider/rpc.ts b/packages/evm/src/provider/rpc.ts index 2f188bb1..047e9c99 100644 --- a/packages/evm/src/provider/rpc.ts +++ b/packages/evm/src/provider/rpc.ts @@ -169,6 +169,10 @@ export const ChainMetadata: { [key: number]: ChainMetadataType } = { }, } +export function findChainDataByChainId(chainId: number): RpcType | undefined { + return Object.values(ChainData).find((chain) => hexToDec(chain.chainId) === chainId) +} + export function findChainMetadataByChainId(chainId: number): ChainMetadataType | undefined { return Object.values(ChainMetadata).find((chain) => hexToDec(chain.chainId) === chainId) } @@ -188,6 +192,7 @@ export class JsonRPCWallet extends BaseProviderWallet { } public async connect(): Promise { + console.log('Trying connect') try { const connected = await this.isConnected() if (!connected) { @@ -217,10 +222,12 @@ export class JsonRPCWallet extends BaseProviderWallet { } public async changeNetwork(chainOrRpc: RpcType | RpcId = RpcId.ETH): Promise { - if (typeof chainOrRpc === 'number') { - await this.provider.send('wallet_switchEthereumChain', [{ chainId: ChainData[chainOrRpc].chainId }]) - } else { - await this.provider.send('wallet_switchEthereumChain', [{ chainId: chainOrRpc }]) + const chainData = typeof chainOrRpc === 'number' ? ChainData[chainOrRpc] : chainOrRpc + + try { + await this.provider.send('wallet_switchEthereumChain', [{ chainId: chainData.chainId }]) + } catch (error) { + await this.addNetwork(chainData) } } @@ -237,4 +244,12 @@ export class JsonRPCWallet extends BaseProviderWallet { const accounts = await this.provider.send('eth_accounts', []) return accounts.length !== 0 } + + private async addNetwork(chainData: RpcType): Promise { + try { + await this.provider.send('wallet_addEthereumChain', [chainData]) + } catch (error) { + throw new Error(`Could not add network to provider: ${error}`) + } + } } From d0566bec4c76ccd30791f1ac1044ead1fd31b8a5 Mon Sep 17 00:00:00 2001 From: philogicae Date: Thu, 5 Sep 2024 13:53:49 +0300 Subject: [PATCH 09/10] Fix: message.chain=ETH for all EVM chains messages (else rejected) --- package-lock.json | 3 ++- packages/message/package.json | 3 ++- packages/message/src/utils/messageBuilder.ts | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2026de2..6d412ead 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29990,7 +29990,8 @@ }, "peerDependencies": { "@aleph-sdk/account": "^1.x.x", - "@aleph-sdk/core": "^1.x.x" + "@aleph-sdk/core": "^1.x.x", + "@aleph-sdk/evm": "^1.x.x" } }, "packages/nuls2": { diff --git a/packages/message/package.json b/packages/message/package.json index 7814310f..4f2d5dd5 100644 --- a/packages/message/package.json +++ b/packages/message/package.json @@ -21,7 +21,8 @@ }, "peerDependencies": { "@aleph-sdk/account": "^1.x.x", - "@aleph-sdk/core": "^1.x.x" + "@aleph-sdk/core": "^1.x.x", + "@aleph-sdk/evm": "^1.x.x" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", diff --git a/packages/message/src/utils/messageBuilder.ts b/packages/message/src/utils/messageBuilder.ts index 5c83d88f..0c29e94e 100644 --- a/packages/message/src/utils/messageBuilder.ts +++ b/packages/message/src/utils/messageBuilder.ts @@ -1,4 +1,6 @@ import { Account } from '@aleph-sdk/account' +import { Blockchain } from '@aleph-sdk/core' +import { EVMAccount } from '@aleph-sdk/evm' import { BuiltMessage, ItemType, MessageContent, MessageType } from '../types' import { PostContent } from '../post' import { AggregateContent } from '../aggregate' @@ -17,7 +19,7 @@ export type MessageBuilderConfig = { function buildMessage(config: MessageBuilderConfig, type: MessageType): BuiltMessage { return new BuiltMessage({ - chain: config.account.getChain(), + chain: config.account instanceof EVMAccount ? Blockchain.ETH : config.account.getChain(), sender: config.account.address, channel: config.channel, time: config.timestamp, From 29a2481c8eb4a8f70e2371e9f7af158c48474e8c Mon Sep 17 00:00:00 2001 From: philogicae Date: Thu, 5 Sep 2024 13:54:09 +0300 Subject: [PATCH 10/10] Fix: message.chain=ETH for all EVM chains messages (else rejected) --- packages/avalanche/src/account.ts | 16 +++++++++------- packages/base/src/account.ts | 13 ++++++++----- packages/ethereum/src/account.ts | 14 ++++++++------ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/packages/avalanche/src/account.ts b/packages/avalanche/src/account.ts index d9c33b4a..f3f21d18 100644 --- a/packages/avalanche/src/account.ts +++ b/packages/avalanche/src/account.ts @@ -3,10 +3,9 @@ import { KeyPair, KeyChain } from 'avalanche/dist/apis/avm' import { KeyPair as EVMKeyPair } from 'avalanche/dist/apis/evm' import { ethers, providers } from 'ethers' import { privateToAddress } from 'ethereumjs-util' - import { Blockchain } from '@aleph-sdk/core' import { SignableMessage, BaseProviderWallet } from '@aleph-sdk/account' -import { ChangeRpcParam, RpcId, EVMAccount, JsonRPCWallet } from '@aleph-sdk/evm' +import { ChangeRpcParam, RpcId, EVMAccount, JsonRPCWallet, ChainData } from '@aleph-sdk/evm' import { digestMessage, verifyAvalanche } from './verify' /** @@ -152,18 +151,21 @@ export async function importAccountFromMnemonic( /** * Get an account from a Web3 provider (ex: Metamask) * - * @param {providers.ExternalProvider} provider from metamask + * @param {providers.ExternalProvider | ethers.providers.Web3Provider} provider * @param requestedRpc Use this params to change the RPC endpoint; */ export async function getAccountFromProvider( - provider: providers.ExternalProvider, + provider: ethers.providers.ExternalProvider | ethers.providers.Web3Provider, requestedRpc: ChangeRpcParam = RpcId.AVAX, ): Promise { - const avaxProvider = new providers.Web3Provider(provider) - const jrw = new JsonRPCWallet(avaxProvider) - await jrw.changeNetwork(requestedRpc) + const ETHprovider = + provider instanceof ethers.providers.Web3Provider ? provider : new providers.Web3Provider(provider) + const jrw = new JsonRPCWallet(ETHprovider) + const chainId = Number((typeof requestedRpc === 'number' ? ChainData[requestedRpc] : requestedRpc).chainId) + if (chainId !== (await jrw.provider.getNetwork()).chainId) await jrw.changeNetwork(requestedRpc) await jrw.connect() + if (jrw.address) { return new AvalancheAccount(jrw, jrw.address) } diff --git a/packages/base/src/account.ts b/packages/base/src/account.ts index 185f36e1..f11df3cb 100644 --- a/packages/base/src/account.ts +++ b/packages/base/src/account.ts @@ -2,7 +2,7 @@ import * as bip39 from 'bip39' import { Blockchain } from '@aleph-sdk/core' import { ETHAccount } from '@aleph-sdk/ethereum' import { ethers } from 'ethers' -import { ChangeRpcParam, JsonRPCWallet, RpcId } from '@aleph-sdk/evm' +import { ChainData, ChangeRpcParam, JsonRPCWallet, RpcId } from '@aleph-sdk/evm' /** * BaseAccount implements the Account class for the Base protocol. @@ -55,16 +55,19 @@ export function newAccount(derivationPath = "m/44'/60'/0'/0/0"): { account: Base /** * Get an account from a Web3 provider (ex: Metamask) * - * @param {ethers.providers.ExternalProvider} provider + * @param {ethers.providers.ExternalProvider | ethers.providers.Web3Provider} provider * @param requestedRpc Use this params to change the RPC endpoint; */ export async function getAccountFromProvider( - provider: ethers.providers.ExternalProvider, + provider: ethers.providers.ExternalProvider | ethers.providers.Web3Provider, requestedRpc: ChangeRpcParam = RpcId.BASE, ): Promise { - const ETHprovider = new ethers.providers.Web3Provider(provider) + const ETHprovider = + provider instanceof ethers.providers.Web3Provider ? provider : new ethers.providers.Web3Provider(provider) const jrw = new JsonRPCWallet(ETHprovider) - await jrw.changeNetwork(requestedRpc) + + const chainId = Number((typeof requestedRpc === 'number' ? ChainData[requestedRpc] : requestedRpc).chainId) + if (chainId !== (await jrw.provider.getNetwork()).chainId) await jrw.changeNetwork(requestedRpc) await jrw.connect() if (jrw.address) return new BaseAccount(jrw, jrw.address) diff --git a/packages/ethereum/src/account.ts b/packages/ethereum/src/account.ts index ed11e648..cdecc566 100644 --- a/packages/ethereum/src/account.ts +++ b/packages/ethereum/src/account.ts @@ -1,9 +1,8 @@ import * as bip39 from 'bip39' import { ethers } from 'ethers' - import { Blockchain } from '@aleph-sdk/core' import { SignableMessage, BaseProviderWallet } from '@aleph-sdk/account' -import { ChangeRpcParam, JsonRPCWallet, RpcId, EVMAccount } from '@aleph-sdk/evm' +import { ChangeRpcParam, JsonRPCWallet, RpcId, EVMAccount, ChainData } from '@aleph-sdk/evm' /** * ETHAccount implements the Account class for the Ethereum protocol. @@ -96,16 +95,19 @@ export function newAccount(derivationPath = "m/44'/60'/0'/0/0"): { account: ETHA /** * Get an account from a Web3 provider (ex: Metamask) * - * @param {ethers.providers.ExternalProvider} provider + * @param {ethers.providers.ExternalProvider | ethers.providers.Web3Provider} provider * @param requestedRpc Use this params to change the RPC endpoint; */ export async function getAccountFromProvider( - provider: ethers.providers.ExternalProvider, + provider: ethers.providers.ExternalProvider | ethers.providers.Web3Provider, requestedRpc: ChangeRpcParam = RpcId.ETH, ): Promise { - const ETHprovider = new ethers.providers.Web3Provider(provider) + const ETHprovider = + provider instanceof ethers.providers.Web3Provider ? provider : new ethers.providers.Web3Provider(provider) const jrw = new JsonRPCWallet(ETHprovider) - await jrw.changeNetwork(requestedRpc) + + const chainId = Number((typeof requestedRpc === 'number' ? ChainData[requestedRpc] : requestedRpc).chainId) + if (chainId !== (await jrw.provider.getNetwork()).chainId) await jrw.changeNetwork(requestedRpc) await jrw.connect() if (jrw.address) return new ETHAccount(jrw, jrw.address)