diff --git a/apps/armory/src/shared/core/resource/chain-registry.json b/apps/armory/src/shared/core/resource/chain-registry.json index 0d5da97f1..5debe7af3 100644 --- a/apps/armory/src/shared/core/resource/chain-registry.json +++ b/apps/armory/src/shared/core/resource/chain-registry.json @@ -512,4 +512,4 @@ "2713017997578000": { "nativeSlip44": 60 } -} \ No newline at end of file +} diff --git a/apps/armory/src/shared/core/script/build-chain-registry.ts b/apps/armory/src/shared/core/script/build-chain-registry.ts index 0b4383c57..72b7f6531 100644 --- a/apps/armory/src/shared/core/script/build-chain-registry.ts +++ b/apps/armory/src/shared/core/script/build-chain-registry.ts @@ -1,53 +1,54 @@ -import { ApplicationException } from '../../exception/application.exception' -import { extractChain } from 'viem' +import * as fs from 'fs' +import { ChainRegistry } from 'packages/transaction-request-intent/src/lib/registry/chain-registry' +import * as path from 'path' +import { registeredCoinTypes } from 'slip44' import * as chainObject from 'viem/chains' -import { registeredCoinTypes } from 'slip44'; -import { HttpStatus } from '@nestjs/common'; -import { ChainRegistry } from '@narval/transaction-request-intent'; -import * as fs from 'fs'; -import * as path from 'path'; + +/* eslint-disable no-console */ +/* eslint-disable no-unused-vars */ export const buildNativeSlip44 = (): ChainRegistry => { - const chains = Object.values(chainObject); + const chains = Object.values(chainObject) - let count = 0; - const chainRegistry: ChainRegistry = new Map(); + let count = 0 + const chainRegistry: ChainRegistry = new Map() chains.forEach((chain) => { - const tokenSlip44 = registeredCoinTypes.find(([ - _coinType, - _derivationPathComponent, - symbol, - _name, - ]) => { - return symbol === chain.nativeCurrency.symbol; - }); + const tokenSlip44 = registeredCoinTypes.find(([_coinType, _derivationPathComponent, symbol, _name]) => { + return symbol === chain.nativeCurrency.symbol + }) if (tokenSlip44) { chainRegistry.set(chain.id, { - nativeSlip44: tokenSlip44[0], - }); + nativeSlip44: tokenSlip44[0] + }) } else { - console.error(`Slip44 not found for native token: ${JSON.stringify({ - symbol: chain.nativeCurrency.symbol, - chainId: chain.id, - name: chain.name - }, null, 2)}`); - count++; + console.error( + `Slip44 not found for native token: ${JSON.stringify( + { + symbol: chain.nativeCurrency.symbol, + chainId: chain.id, + name: chain.name + }, + null, + 2 + )}` + ) + count++ } - }); + }) - console.log(`Slip44 not found for ${count} native tokens`); - return chainRegistry; + console.log(`Slip44 not found for ${count} native tokens`) + return chainRegistry } const main = () => { - const fileName = 'chain-registry.json'; - const relativePath = '../resource'; - const filePath = path.resolve(__dirname, relativePath, fileName); + const fileName = 'chain-registry.json' + const relativePath = '../resource' + const filePath = path.resolve(__dirname, relativePath, fileName) - const registry = buildNativeSlip44(); - const obj = Object.fromEntries(registry); - fs.writeFileSync(filePath, JSON.stringify(obj, null, 2), 'utf8'); - console.log(`JSON written to ${filePath}`); -}; + const registry = buildNativeSlip44() + const obj = Object.fromEntries(registry) + fs.writeFileSync(filePath, JSON.stringify(obj, null, 2), 'utf8') + console.log(`JSON written to ${filePath}`) +} -main(); +main() diff --git a/packages/transaction-request-intent/src/lib/__test__/default-chains.ts b/packages/transaction-request-intent/src/lib/__test__/default-chains.ts index 28bc787be..282ca088d 100644 --- a/packages/transaction-request-intent/src/lib/__test__/default-chains.ts +++ b/packages/transaction-request-intent/src/lib/__test__/default-chains.ts @@ -1,515 +1,515 @@ -export const chains = { - "1": { - "nativeSlip44": 60 +export const CHAINS = { + '1': { + nativeSlip44: 60 }, - "5": { - "nativeSlip44": 60 + '5': { + nativeSlip44: 60 }, - "10": { - "nativeSlip44": 60 + '10': { + nativeSlip44: 60 }, - "25": { - "nativeSlip44": 394 + '25': { + nativeSlip44: 394 }, - "30": { - "nativeSlip44": 137 + '30': { + nativeSlip44: 137 }, - "40": { - "nativeSlip44": 977 + '40': { + nativeSlip44: 977 }, - "41": { - "nativeSlip44": 977 + '41': { + nativeSlip44: 977 }, - "50": { - "nativeSlip44": 550 + '50': { + nativeSlip44: 550 }, - "56": { - "nativeSlip44": 714 + '56': { + nativeSlip44: 714 }, - "57": { - "nativeSlip44": 57 + '57': { + nativeSlip44: 57 }, - "61": { - "nativeSlip44": 61 + '61': { + nativeSlip44: 61 }, - "66": { - "nativeSlip44": 996 + '66': { + nativeSlip44: 996 }, - "71": { - "nativeSlip44": 503 + '71': { + nativeSlip44: 503 }, - "82": { - "nativeSlip44": 93 + '82': { + nativeSlip44: 93 }, - "83": { - "nativeSlip44": 93 + '83': { + nativeSlip44: 93 }, - "137": { - "nativeSlip44": 966 + '137': { + nativeSlip44: 966 }, - "148": { - "nativeSlip44": 4219 + '148': { + nativeSlip44: 4219 }, - "153": { - "nativeSlip44": 824 + '153': { + nativeSlip44: 824 }, - "169": { - "nativeSlip44": 60 + '169': { + nativeSlip44: 60 }, - "199": { - "nativeSlip44": 34952 + '199': { + nativeSlip44: 34952 }, - "204": { - "nativeSlip44": 714 + '204': { + nativeSlip44: 714 }, - "248": { - "nativeSlip44": 685 + '248': { + nativeSlip44: 685 }, - "250": { - "nativeSlip44": 1007 + '250': { + nativeSlip44: 1007 }, - "255": { - "nativeSlip44": 60 + '255': { + nativeSlip44: 60 }, - "260": { - "nativeSlip44": 60 + '260': { + nativeSlip44: 60 }, - "270": { - "nativeSlip44": 60 + '270': { + nativeSlip44: 60 }, - "280": { - "nativeSlip44": 60 + '280': { + nativeSlip44: 60 }, - "288": { - "nativeSlip44": 60 + '288': { + nativeSlip44: 60 }, - "295": { - "nativeSlip44": 3030 + '295': { + nativeSlip44: 3030 }, - "296": { - "nativeSlip44": 3030 + '296': { + nativeSlip44: 3030 }, - "297": { - "nativeSlip44": 3030 + '297': { + nativeSlip44: 3030 }, - "300": { - "nativeSlip44": 60 + '300': { + nativeSlip44: 60 }, - "314": { - "nativeSlip44": 461 + '314': { + nativeSlip44: 461 }, - "321": { - "nativeSlip44": 641 + '321': { + nativeSlip44: 641 }, - "324": { - "nativeSlip44": 60 + '324': { + nativeSlip44: 60 }, - "369": { - "nativeSlip44": 1028 + '369': { + nativeSlip44: 1028 }, - "420": { - "nativeSlip44": 60 + '420': { + nativeSlip44: 60 }, - "424": { - "nativeSlip44": 60 + '424': { + nativeSlip44: 60 }, - "545": { - "nativeSlip44": 539 + '545': { + nativeSlip44: 539 }, - "570": { - "nativeSlip44": 57 + '570': { + nativeSlip44: 57 }, - "571": { - "nativeSlip44": 601 + '571': { + nativeSlip44: 601 }, - "592": { - "nativeSlip44": 810 + '592': { + nativeSlip44: 810 }, - "646": { - "nativeSlip44": 539 + '646': { + nativeSlip44: 539 }, - "686": { - "nativeSlip44": 686 + '686': { + nativeSlip44: 686 }, - "690": { - "nativeSlip44": 60 + '690': { + nativeSlip44: 60 }, - "747": { - "nativeSlip44": 539 + '747': { + nativeSlip44: 539 }, - "787": { - "nativeSlip44": 787 + '787': { + nativeSlip44: 787 }, - "888": { - "nativeSlip44": 5718350 + '888': { + nativeSlip44: 5718350 }, - "919": { - "nativeSlip44": 60 + '919': { + nativeSlip44: 60 }, - "999": { - "nativeSlip44": 60 + '999': { + nativeSlip44: 60 }, - "1001": { - "nativeSlip44": 8217 + '1001': { + nativeSlip44: 8217 }, - "1004": { - "nativeSlip44": 656 + '1004': { + nativeSlip44: 656 }, - "1017": { - "nativeSlip44": 714 + '1017': { + nativeSlip44: 714 }, - "1028": { - "nativeSlip44": 34952 + '1028': { + nativeSlip44: 34952 }, - "1030": { - "nativeSlip44": 503 + '1030': { + nativeSlip44: 503 }, - "1073": { - "nativeSlip44": 4219 + '1073': { + nativeSlip44: 4219 }, - "1101": { - "nativeSlip44": 60 + '1101': { + nativeSlip44: 60 }, - "1116": { - "nativeSlip44": 990 + '1116': { + nativeSlip44: 990 }, - "1130": { - "nativeSlip44": 1129 + '1130': { + nativeSlip44: 1129 }, - "1131": { - "nativeSlip44": 1129 + '1131': { + nativeSlip44: 1129 }, - "1135": { - "nativeSlip44": 60 + '1135': { + nativeSlip44: 60 }, - "1284": { - "nativeSlip44": 1284 + '1284': { + nativeSlip44: 1284 }, - "1285": { - "nativeSlip44": 1285 + '1285': { + nativeSlip44: 1285 }, - "1337": { - "nativeSlip44": 60 + '1337': { + nativeSlip44: 60 }, - "1442": { - "nativeSlip44": 60 + '1442': { + nativeSlip44: 60 }, - "1453": { - "nativeSlip44": 601 + '1453': { + nativeSlip44: 601 }, - "1686": { - "nativeSlip44": 60 + '1686': { + nativeSlip44: 60 }, - "1729": { - "nativeSlip44": 60 + '1729': { + nativeSlip44: 60 }, - "1750": { - "nativeSlip44": 60 + '1750': { + nativeSlip44: 60 }, - "1890": { - "nativeSlip44": 60 + '1890': { + nativeSlip44: 60 }, - "1891": { - "nativeSlip44": 60 + '1891': { + nativeSlip44: 60 }, - "1994": { - "nativeSlip44": 656 + '1994': { + nativeSlip44: 656 }, - "2021": { - "nativeSlip44": 523 + '2021': { + nativeSlip44: 523 }, - "2221": { - "nativeSlip44": 459 + '2221': { + nativeSlip44: 459 }, - "2222": { - "nativeSlip44": 459 + '2222': { + nativeSlip44: 459 }, - "2358": { - "nativeSlip44": 60 + '2358': { + nativeSlip44: 60 }, - "2442": { - "nativeSlip44": 60 + '2442': { + nativeSlip44: 60 }, - "2710": { - "nativeSlip44": 60 + '2710': { + nativeSlip44: 60 }, - "3109": { - "nativeSlip44": 0 + '3109': { + nativeSlip44: 0 }, - "3110": { - "nativeSlip44": 0 + '3110': { + nativeSlip44: 0 }, - "3737": { - "nativeSlip44": 773 + '3737': { + nativeSlip44: 773 }, - "3776": { - "nativeSlip44": 60 + '3776': { + nativeSlip44: 60 }, - "3993": { - "nativeSlip44": 60 + '3993': { + nativeSlip44: 60 }, - "4002": { - "nativeSlip44": 1007 + '4002': { + nativeSlip44: 1007 }, - "4200": { - "nativeSlip44": 0 + '4200': { + nativeSlip44: 0 }, - "4202": { - "nativeSlip44": 60 + '4202': { + nativeSlip44: 60 }, - "4242": { - "nativeSlip44": 2500 + '4242': { + nativeSlip44: 2500 }, - "4337": { - "nativeSlip44": 1533 + '4337': { + nativeSlip44: 1533 }, - "4689": { - "nativeSlip44": 304 + '4689': { + nativeSlip44: 304 }, - "4690": { - "nativeSlip44": 304 + '4690': { + nativeSlip44: 304 }, - "4759": { - "nativeSlip44": 7518 + '4759': { + nativeSlip44: 7518 }, - "4999": { - "nativeSlip44": 4999 + '4999': { + nativeSlip44: 4999 }, - "5112": { - "nativeSlip44": 60 + '5112': { + nativeSlip44: 60 }, - "5700": { - "nativeSlip44": 57 + '5700': { + nativeSlip44: 57 }, - "7000": { - "nativeSlip44": 7000 + '7000': { + nativeSlip44: 7000 }, - "7332": { - "nativeSlip44": 121 + '7332': { + nativeSlip44: 121 }, - "7518": { - "nativeSlip44": 7518 + '7518': { + nativeSlip44: 7518 }, - "7560": { - "nativeSlip44": 60 + '7560': { + nativeSlip44: 60 }, - "8082": { - "nativeSlip44": 95 + '8082': { + nativeSlip44: 95 }, - "8217": { - "nativeSlip44": 8217 + '8217': { + nativeSlip44: 8217 }, - "8453": { - "nativeSlip44": 60 + '8453': { + nativeSlip44: 60 }, - "11235": { - "nativeSlip44": 1348 + '11235': { + nativeSlip44: 1348 }, - "11501": { - "nativeSlip44": 0 + '11501': { + nativeSlip44: 0 }, - "12324": { - "nativeSlip44": 60 + '12324': { + nativeSlip44: 60 }, - "12325": { - "nativeSlip44": 60 + '12325': { + nativeSlip44: 60 }, - "13337": { - "nativeSlip44": 1533 + '13337': { + nativeSlip44: 1533 }, - "13381": { - "nativeSlip44": 13381 + '13381': { + nativeSlip44: 13381 }, - "15557": { - "nativeSlip44": 194 + '15557': { + nativeSlip44: 194 }, - "17000": { - "nativeSlip44": 60 + '17000': { + nativeSlip44: 60 }, - "17777": { - "nativeSlip44": 194 + '17777': { + nativeSlip44: 194 }, - "22222": { - "nativeSlip44": 883 + '22222': { + nativeSlip44: 883 }, - "23294": { - "nativeSlip44": 474 + '23294': { + nativeSlip44: 474 }, - "31337": { - "nativeSlip44": 60 + '31337': { + nativeSlip44: 60 }, - "32769": { - "nativeSlip44": 313 + '32769': { + nativeSlip44: 313 }, - "33101": { - "nativeSlip44": 313 + '33101': { + nativeSlip44: 313 }, - "34443": { - "nativeSlip44": 60 + '34443': { + nativeSlip44: 60 }, - "42161": { - "nativeSlip44": 60 + '42161': { + nativeSlip44: 60 }, - "42170": { - "nativeSlip44": 60 + '42170': { + nativeSlip44: 60 }, - "42220": { - "nativeSlip44": 52752 + '42220': { + nativeSlip44: 52752 }, - "43113": { - "nativeSlip44": 9000 + '43113': { + nativeSlip44: 9000 }, - "43114": { - "nativeSlip44": 9000 + '43114': { + nativeSlip44: 9000 }, - "48899": { - "nativeSlip44": 60 + '48899': { + nativeSlip44: 60 }, - "50005": { - "nativeSlip44": 685 + '50005': { + nativeSlip44: 685 }, - "50006": { - "nativeSlip44": 685 + '50006': { + nativeSlip44: 685 }, - "57000": { - "nativeSlip44": 57 + '57000': { + nativeSlip44: 57 }, - "58008": { - "nativeSlip44": 60 + '58008': { + nativeSlip44: 60 }, - "59140": { - "nativeSlip44": 60 + '59140': { + nativeSlip44: 60 }, - "59141": { - "nativeSlip44": 60 + '59141': { + nativeSlip44: 60 }, - "59144": { - "nativeSlip44": 60 + '59144': { + nativeSlip44: 60 }, - "60808": { - "nativeSlip44": 60 + '60808': { + nativeSlip44: 60 }, - "64240": { - "nativeSlip44": 1007 + '64240': { + nativeSlip44: 1007 }, - "80001": { - "nativeSlip44": 966 + '80001': { + nativeSlip44: 966 }, - "80002": { - "nativeSlip44": 966 + '80002': { + nativeSlip44: 966 }, - "81457": { - "nativeSlip44": 60 + '81457': { + nativeSlip44: 60 }, - "84531": { - "nativeSlip44": 60 + '84531': { + nativeSlip44: 60 }, - "84532": { - "nativeSlip44": 60 + '84532': { + nativeSlip44: 60 }, - "100009": { - "nativeSlip44": 818 + '100009': { + nativeSlip44: 818 }, - "105105": { - "nativeSlip44": 105105 + '105105': { + nativeSlip44: 105105 }, - "128123": { - "nativeSlip44": 1729 + '128123': { + nativeSlip44: 1729 }, - "167000": { - "nativeSlip44": 60 + '167000': { + nativeSlip44: 60 }, - "167005": { - "nativeSlip44": 60 + '167005': { + nativeSlip44: 60 }, - "167007": { - "nativeSlip44": 60 + '167007': { + nativeSlip44: 60 }, - "167008": { - "nativeSlip44": 60 + '167008': { + nativeSlip44: 60 }, - "167009": { - "nativeSlip44": 60 + '167009': { + nativeSlip44: 60 }, - "200810": { - "nativeSlip44": 60 + '200810': { + nativeSlip44: 60 }, - "200901": { - "nativeSlip44": 60 + '200901': { + nativeSlip44: 60 }, - "421613": { - "nativeSlip44": 60 + '421613': { + nativeSlip44: 60 }, - "421614": { - "nativeSlip44": 60 + '421614': { + nativeSlip44: 60 }, - "534351": { - "nativeSlip44": 60 + '534351': { + nativeSlip44: 60 }, - "534352": { - "nativeSlip44": 60 + '534352': { + nativeSlip44: 60 }, - "1612127": { - "nativeSlip44": 60 + '1612127': { + nativeSlip44: 60 }, - "3397901": { - "nativeSlip44": 60 + '3397901': { + nativeSlip44: 60 }, - "3441005": { - "nativeSlip44": 60 + '3441005': { + nativeSlip44: 60 }, - "3441006": { - "nativeSlip44": 60 + '3441006': { + nativeSlip44: 60 }, - "6038361": { - "nativeSlip44": 60 + '6038361': { + nativeSlip44: 60 }, - "7777777": { - "nativeSlip44": 60 + '7777777': { + nativeSlip44: 60 }, - "11155111": { - "nativeSlip44": 60 + '11155111': { + nativeSlip44: 60 }, - "11155420": { - "nativeSlip44": 60 + '11155420': { + nativeSlip44: 60 }, - "28122024": { - "nativeSlip44": 60 + '28122024': { + nativeSlip44: 60 }, - "41144114": { - "nativeSlip44": 60 + '41144114': { + nativeSlip44: 60 }, - "111557560": { - "nativeSlip44": 60 + '111557560': { + nativeSlip44: 60 }, - "161221135": { - "nativeSlip44": 60 + '161221135': { + nativeSlip44: 60 }, - "168587773": { - "nativeSlip44": 60 + '168587773': { + nativeSlip44: 60 }, - "888888888": { - "nativeSlip44": 60 + '888888888': { + nativeSlip44: 60 }, - "999999999": { - "nativeSlip44": 60 + '999999999': { + nativeSlip44: 60 }, - "1313161554": { - "nativeSlip44": 60 + '1313161554': { + nativeSlip44: 60 }, - "1313161555": { - "nativeSlip44": 60 + '1313161555': { + nativeSlip44: 60 }, - "1666600000": { - "nativeSlip44": 270 + '1666600000': { + nativeSlip44: 270 }, - "1802203764": { - "nativeSlip44": 60 + '1802203764': { + nativeSlip44: 60 }, - "2716446429837000": { - "nativeSlip44": 60 + '2716446429837000': { + nativeSlip44: 60 }, - "2713017997578000": { - "nativeSlip44": 60 + '2713017997578000': { + nativeSlip44: 60 } -} \ No newline at end of file +} diff --git a/packages/transaction-request-intent/src/lib/__test__/unit/decoders.spec.ts b/packages/transaction-request-intent/src/lib/__test__/unit/decoders.spec.ts index c86e4e27c..d98391379 100644 --- a/packages/transaction-request-intent/src/lib/__test__/unit/decoders.spec.ts +++ b/packages/transaction-request-intent/src/lib/__test__/unit/decoders.spec.ts @@ -1,16 +1,11 @@ import { Alg } from '@narval/signature' import { Hex } from 'viem' import { decode } from '../../decoders/decode' -import { - ContractRegistry, - InputType, - Intents, - MessageInput, - PERMIT2_ADDRESS, - TransactionStatus, - WalletType -} from '../../domain' -import { buildContractRegistry, buildTransactionKey, buildTransactionRegistry } from '../../utils' +import { InputType, Intents, MessageInput, PERMIT2_ADDRESS, TransactionStatus, WalletType } from '../../domain' +import { ChainData, ChainId } from '../../registry/chain-registry' +import { ContractRegistryInput } from '../../registry/contract-registry' +import { TransactionRegistryInput } from '../../registry/transaction-registry' +import { CHAINS } from '../default-chains' import { defaultChainRegistry, mockCancelTransaction, @@ -21,7 +16,6 @@ import { } from './mocks' describe('decode', () => { - const transactionRegistry = buildTransactionRegistry([]) describe('transaction request input', () => { describe('transfers', () => { it('decodes erc20 transfer', () => { @@ -130,9 +124,8 @@ describe('decode', () => { }) }) it('decodes a native transfer using config', () => { - const registry = defaultChainRegistry() - registry.forEach((data, chainId) => { + registry.forEach((data: ChainData, chainId: ChainId) => { const decoded = decode({ input: { type: InputType.TRANSACTION_REQUEST, @@ -144,8 +137,8 @@ describe('decode', () => { nonce: 10 } }, - config: { - chainRegistry: registry + configInput: { + chainRegistryInput: CHAINS } }) expect(decoded).toEqual({ @@ -160,29 +153,20 @@ describe('decode', () => { }) }) describe('transaction management', () => { - // SET A FAILED TO TRANSACTION ON FIRST MOCK DATA - const key = buildTransactionKey(mockErc20Transfer.input.txRequest) - transactionRegistry.set(key, TransactionStatus.FAILED) - it('should find the transaction in the registry', () => { - const trxStatus = transactionRegistry.get(buildTransactionKey(mockErc20Transfer.input.txRequest)) - expect(trxStatus).toEqual(TransactionStatus.FAILED) - }) - // NOW ITS A PENDING it('decodes retry transaction', () => { - const trxRegistry = buildTransactionRegistry([ + const transactionRegistryInput: TransactionRegistryInput = [ { txRequest: mockErc20Transfer.input.txRequest, - status: TransactionStatus.PENDING + status: TransactionStatus.FAILED } - ]) + ] const decoded = decode({ input: { type: InputType.TRANSACTION_REQUEST, txRequest: mockErc20Transfer.input.txRequest }, - config: { - transactionRegistry: trxRegistry, - chainRegistry: defaultChainRegistry() + configInput: { + transactionRegistryInput } }) expect(decoded).toEqual({ @@ -190,52 +174,53 @@ describe('decode', () => { }) }) it('decodes cancel transaction', () => { - transactionRegistry.set(key, TransactionStatus.PENDING) - const decoded = decode({ input: mockCancelTransaction }) + const transactionRegistryInput: TransactionRegistryInput = [ + { + txRequest: mockErc20Transfer.input.txRequest, + status: TransactionStatus.PENDING + } + ] + const decoded = decode({ input: mockCancelTransaction, configInput: { transactionRegistryInput } }) expect(decoded).toEqual({ type: Intents.CANCEL_TRANSACTION }) }) }) describe('contract creation', () => { - let contractRegistry: ContractRegistry - const knownSafeFactory = '0xaaad8C0cA142921c459bCB28104c0FF37928F9eD' const knownSafeMaster = '0xbbbd8C0cA142921c459bCB28104c0FF37928F9eD' const knownErc4337Factory = '0xcccd8C0cA142921c459bCB28104c0FF37928F9eD' const knownErc4337Master = '0xdddd8C0cA142921c459bCB28104c0FF37928F9eD' - beforeEach(() => { - contractRegistry = buildContractRegistry([ - { - contract: { - address: knownSafeFactory, - chainId: 137 - }, - factoryType: WalletType.SAFE + const contractRegistryInput: ContractRegistryInput = [ + { + contract: { + address: knownSafeFactory, + chainId: 137 }, - { - contract: { - address: knownSafeMaster, - chainId: 1 - }, - factoryType: WalletType.SAFE + factoryType: WalletType.SAFE + }, + { + contract: { + address: knownSafeMaster, + chainId: 1 }, - { - contract: { - address: knownErc4337Factory, - chainId: 137 - }, - factoryType: WalletType.ERC4337 + factoryType: WalletType.SAFE + }, + { + contract: { + address: knownErc4337Factory, + chainId: 137 }, - { - contract: { - address: knownErc4337Master, - chainId: 1 - }, - factoryType: WalletType.ERC4337 - } - ]) - }) + factoryType: WalletType.ERC4337 + }, + { + contract: { + address: knownErc4337Master, + chainId: 1 + }, + factoryType: WalletType.ERC4337 + } + ] it('decodes safe wallet creation deployment from a known factory', () => { const decoded = decode({ input: { @@ -246,10 +231,8 @@ describe('decode', () => { data: '0x41284124120948012849081209470127490127940790127490712038017403178947109247' } }, - config: { - contractRegistry, - chainRegistry: defaultChainRegistry() - + configInput: { + contractRegistryInput } }) expect(decoded).toEqual({ @@ -268,9 +251,8 @@ describe('decode', () => { data: '0x41284124120948012849081209470127490127940790127490712038017403178947109247' } }, - config: { - contractRegistry, - chainRegistry: defaultChainRegistry() + configInput: { + contractRegistryInput } }) expect(decoded).toEqual({ @@ -290,9 +272,8 @@ describe('decode', () => { data: '0x' } }, - config: { - contractRegistry, - chainRegistry: defaultChainRegistry() + configInput: { + contractRegistryInput } }) expect(decoded).toEqual({ diff --git a/packages/transaction-request-intent/src/lib/__test__/unit/mocks.ts b/packages/transaction-request-intent/src/lib/__test__/unit/mocks.ts index abd147190..2b03fcba4 100644 --- a/packages/transaction-request-intent/src/lib/__test__/unit/mocks.ts +++ b/packages/transaction-request-intent/src/lib/__test__/unit/mocks.ts @@ -5,10 +5,10 @@ import { getAssetId, getChainAccountId } from '@narval/policy-engine-shared' -import { ChainRegistry, DecodeInput, InputType, Intents, TransactionInput } from '../../domain' +import { DecodeInput, InputType, Intents, TransactionInput } from '../../domain' import { TransferErc1155, TransferErc20, TransferErc721, TransferNative } from '../../intent.types' -import { registeredCoinTypes } from 'slip44'; -import { chains } from '../default-chains'; +import { ChainRegistry } from '../../registry/chain-registry' +import { CHAINS } from '../default-chains' export const ONE_ETH = BigInt('1000000000000000000') @@ -378,9 +378,9 @@ export const mockCancelTransaction: DecodeInput = { } export const defaultChainRegistry = (): ChainRegistry => { - const chainRegistry = new ChainRegistry(); - for (const [key, value] of Object.entries(chains)) { - chainRegistry.set(+key, value); + const chainRegistry = new ChainRegistry() + for (const [key, value] of Object.entries(CHAINS)) { + chainRegistry.set(+key, value) } - return chainRegistry; -} \ No newline at end of file + return chainRegistry +} diff --git a/packages/transaction-request-intent/src/lib/decoders/decode.ts b/packages/transaction-request-intent/src/lib/decoders/decode.ts index 6e3342a8a..08a05a3ad 100644 --- a/packages/transaction-request-intent/src/lib/decoders/decode.ts +++ b/packages/transaction-request-intent/src/lib/decoders/decode.ts @@ -1,21 +1,23 @@ -import { defaultChainRegistry } from '../__test__/unit/mocks' +import { toChainAccountId } from '@narval/policy-engine-shared' +import { CHAINS } from '../__test__/default-chains' import { - ChainRegistry, Config, + ConfigInput, ContractCallInput, - ContractRegistry, DecodeInput, InputType, Intents, SafeDecodeOutput, TransactionCategory, TransactionInput, - TransactionRegistry, TransactionStatus, TypedDataInput } from '../domain' import { DecoderError } from '../error' import { Intent, TypedDataIntent } from '../intent.types' +import { ChainRegistry } from '../registry/chain-registry' +import { ContractRegistry } from '../registry/contract-registry' +import { TransactionKey, TransactionRegistry } from '../registry/transaction-registry' import { MethodsMapping, SUPPORTED_METHODS } from '../supported-methods' import { isSupportedMethodId } from '../typeguards' import { @@ -25,8 +27,7 @@ import { decodeTypedData, getCategory, getMethodId, - getTransactionIntentType, - transactionLookup + getTransactionIntentType } from '../utils' import { validateContractDeploymentInput, @@ -42,13 +43,6 @@ import { decodeErc721Transfer } from './transaction/interaction/Erc721TransferDe import { decodeUserOperation } from './transaction/interaction/UserOperationDecoder' import { decodeNativeTransfer } from './transaction/native/NativeTransferDecoder' -const defaultConfig: Config = { - supportedMethods: SUPPORTED_METHODS, - chainRegistry: defaultChainRegistry(), - contractRegistry: undefined, - transactionRegistry: undefined, -} - const decodeContractCall = (input: ContractCallInput, intent: Intents, supportedMethods: MethodsMapping) => { if (!isSupportedMethodId(input.methodId)) { return decodeCallContract(input) @@ -108,9 +102,17 @@ const wrapTransactionManagementIntents = ( transactionRegistry?: TransactionRegistry ): Intent => { const { txRequest } = input - if (!transactionRegistry || !txRequest.nonce || intent.type === Intents.CANCEL_TRANSACTION) return intent - const trxStatus = transactionLookup(txRequest, transactionRegistry) - if (trxStatus === TransactionStatus.PENDING) { + if ( + !transactionRegistry || + transactionRegistry.size === 0 || + !txRequest.nonce || + intent.type === Intents.CANCEL_TRANSACTION + ) + return intent + const { chainId, from, nonce } = txRequest + const key = TransactionKey.parse([toChainAccountId({ chainId, address: from }), nonce]) + const trxStatus = transactionRegistry.get(key) + if (trxStatus === TransactionStatus.FAILED) { return { type: Intents.RETRY_TRANSACTION } @@ -142,8 +144,22 @@ const decodeTypedDataInput = (input: TypedDataInput): TypedDataIntent => { } } -const decode = ({ input, config = defaultConfig }: { input: DecodeInput; config?: Config }): Intent => { - const { supportedMethods = SUPPORTED_METHODS, chainRegistry = defaultChainRegistry(), contractRegistry, transactionRegistry } = config +export const prepareConfig = (config: ConfigInput): Config => { + const { chainRegistryInput, contractRegistryInput, transactionRegistryInput, supportedMethods } = config + const chainRegistry = new ChainRegistry(chainRegistryInput || CHAINS) + const contractRegistry = new ContractRegistry(contractRegistryInput) + const transactionRegistry = new TransactionRegistry(transactionRegistryInput) + return { + chainRegistry, + contractRegistry, + transactionRegistry, + supportedMethods: supportedMethods || SUPPORTED_METHODS + } +} + +const decode = ({ input, configInput = {} }: { input: DecodeInput; configInput?: ConfigInput }): Intent => { + const { chainRegistry, contractRegistry, transactionRegistry, supportedMethods } = prepareConfig(configInput) + switch (input.type) { case InputType.TRANSACTION_REQUEST: { const decoded = decodeTransactionInput(input, supportedMethods, chainRegistry, contractRegistry) @@ -164,9 +180,15 @@ const decode = ({ input, config = defaultConfig }: { input: DecodeInput; config? } } -const safeDecode = ({ input, config = defaultConfig }: { input: DecodeInput; config?: Config }): SafeDecodeOutput => { +const safeDecode = ({ + input, + configInput = {} +}: { + input: DecodeInput + configInput?: ConfigInput +}): SafeDecodeOutput => { try { - const intent = decode({ input, config }) + const intent = decode({ input, configInput }) return { success: true, intent diff --git a/packages/transaction-request-intent/src/lib/decoders/transaction/deployment/DeployContract.ts b/packages/transaction-request-intent/src/lib/decoders/transaction/deployment/DeployContract.ts index 5c4965180..f4d9a0446 100644 --- a/packages/transaction-request-intent/src/lib/decoders/transaction/deployment/DeployContract.ts +++ b/packages/transaction-request-intent/src/lib/decoders/transaction/deployment/DeployContract.ts @@ -1,5 +1,7 @@ -import { ContractDeploymentInput, ContractRegistry, Intents, WalletType } from '../../../domain' +import { Hex } from '@narval/signature' +import { ContractDeploymentInput, Intents, WalletType } from '../../../domain' import { DeployContract, DeployErc4337Wallet, DeploySafeWallet } from '../../../intent.types' +import { ContractRegistry } from '../../../registry/contract-registry' import { contractTypeLookup, toChainAccountIdLowerCase } from '../../../utils' type DeploymentIntent = DeployContract | DeployErc4337Wallet | DeploySafeWallet @@ -9,7 +11,7 @@ export const decodeContractDeployment = ( registry?: ContractRegistry ): DeploymentIntent => { const { from, chainId, data } = input - const fromType = contractTypeLookup(chainId, from, registry) + const fromType = contractTypeLookup(chainId, from.toLowerCase() as Hex, registry) if (fromType?.factoryType === WalletType.SAFE) { // Return a DeploySafeWallet object diff --git a/packages/transaction-request-intent/src/lib/decoders/transaction/interaction/UserOperationDecoder.ts b/packages/transaction-request-intent/src/lib/decoders/transaction/interaction/UserOperationDecoder.ts index acf8abf1f..83edee17c 100644 --- a/packages/transaction-request-intent/src/lib/decoders/transaction/interaction/UserOperationDecoder.ts +++ b/packages/transaction-request-intent/src/lib/decoders/transaction/interaction/UserOperationDecoder.ts @@ -27,7 +27,7 @@ const decodeExecute = (callData: Hex, from: Address, chainId: number, supportedM chainId } }, - config: { + configInput: { supportedMethods } }) @@ -54,7 +54,7 @@ const decodeExecuteAndRevert = ( chainId } }, - config: { + configInput: { supportedMethods } }) diff --git a/packages/transaction-request-intent/src/lib/decoders/transaction/native/NativeTransferDecoder.ts b/packages/transaction-request-intent/src/lib/decoders/transaction/native/NativeTransferDecoder.ts index e4cee5cb5..1d9e54e59 100644 --- a/packages/transaction-request-intent/src/lib/decoders/transaction/native/NativeTransferDecoder.ts +++ b/packages/transaction-request-intent/src/lib/decoders/transaction/native/NativeTransferDecoder.ts @@ -1,8 +1,12 @@ -import { ChainRegistry, Intents, NativeTransferInput } from '../../../domain' +import { Intents, NativeTransferInput } from '../../../domain' import { CancelTransaction, TransferNative } from '../../../intent.types' +import { ChainRegistry } from '../../../registry/chain-registry' import { checkCancelTransaction, nativeCaip19, toChainAccountIdLowerCase } from '../../../utils' -export const decodeNativeTransfer = (input: NativeTransferInput, chains: ChainRegistry): TransferNative | CancelTransaction => { +export const decodeNativeTransfer = ( + input: NativeTransferInput, + chains: ChainRegistry +): TransferNative | CancelTransaction => { const intentType = checkCancelTransaction(input) if (intentType === Intents.CANCEL_TRANSACTION) { return { type: intentType } diff --git a/packages/transaction-request-intent/src/lib/domain.ts b/packages/transaction-request-intent/src/lib/domain.ts index 806f6da28..f681362d2 100644 --- a/packages/transaction-request-intent/src/lib/domain.ts +++ b/packages/transaction-request-intent/src/lib/domain.ts @@ -1,14 +1,9 @@ -import { - Address, - AssetId, - AssetType, - ChainAccountId, - Hex, - TransactionRequest, - type Eip712TypedData -} from '@narval/policy-engine-shared' +import { Address, AssetType, Hex, TransactionRequest, type Eip712TypedData } from '@narval/policy-engine-shared' import { Alg } from '@narval/signature' import { Intent } from './intent.types' +import { ChainRegistry, ChainRegistryInput } from './registry/chain-registry' +import { ContractRegistry, ContractRegistryInput } from './registry/contract-registry' +import { TransactionRegistry, TransactionRegistryInput } from './registry/transaction-registry' import { MethodsMapping } from './supported-methods' export type Raw = { @@ -31,27 +26,6 @@ export type TypedDataInput = { typedData: Eip712TypedData } -export type ContractInformation = { - factoryType: WalletType - assetType: AssetType -} -export type ContractRegistryInput = { - contract: ChainAccountId | { address: Address; chainId: number } - assetType?: AssetType - factoryType?: WalletType -}[] -export type ContractRegistry = Map - -export type TransactionKey = `${ChainAccountId}-${number}` -export type TransactionRegistry = Map - -type ChainId = number -type ChainData = { - nativeSlip44?: number -} - -export class ChainRegistry extends Map {} - export type TransactionInput = { type: InputType.TRANSACTION_REQUEST txRequest: TransactionRequest @@ -86,12 +60,20 @@ export type ValidatedInput = ContractCallInput | NativeTransferInput | ContractD export type ContractInteractionDecoder = (input: ContractCallInput) => Intent export type NativeTransferDecoder = (input: NativeTransferInput) => Intent -export type Config = { - contractRegistry?: ContractRegistry - transactionRegistry?: TransactionRegistry +export type ConfigInput = { + contractRegistryInput?: ContractRegistryInput + transactionRegistryInput?: TransactionRegistryInput supportedMethods?: MethodsMapping - chainRegistry?: ChainRegistry + chainRegistryInput?: ChainRegistryInput } + +export type Config = { + contractRegistry: ContractRegistry + transactionRegistry: TransactionRegistry + supportedMethods: MethodsMapping + chainRegistry: ChainRegistry +} + export type DecodeInput = TransactionInput | MessageInput | RawInput | TypedDataInput type DecodeSuccess = { diff --git a/packages/transaction-request-intent/src/lib/registry/chain-registry.ts b/packages/transaction-request-intent/src/lib/registry/chain-registry.ts new file mode 100644 index 000000000..c92277160 --- /dev/null +++ b/packages/transaction-request-intent/src/lib/registry/chain-registry.ts @@ -0,0 +1,29 @@ +import { z } from 'zod' + +export const ChainId = z.coerce.number().int() +export const ChainData = z.object({ + nativeSlip44: z.coerce.number().int() +}) + +export type ChainId = z.infer +export type ChainData = z.infer + +export const ChainRegistryInput = z.record(z.string(), ChainData) +export type ChainRegistryInput = z.infer + +export class ChainRegistry extends Map { + constructor(input?: ChainRegistryInput) { + if (!input) { + super() + return + } + const validInput = ChainRegistryInput.parse(input) + + const entries = Object.entries(validInput).map(([chainId, chainData]): [ChainId, ChainData] => [ + ChainId.parse(chainId), + ChainData.parse(chainData) + ]) + + super(entries) + } +} diff --git a/packages/transaction-request-intent/src/lib/registry/contract-registry.ts b/packages/transaction-request-intent/src/lib/registry/contract-registry.ts new file mode 100644 index 000000000..065cd61c9 --- /dev/null +++ b/packages/transaction-request-intent/src/lib/registry/contract-registry.ts @@ -0,0 +1,48 @@ +import { addressSchema, AssetType, ChainAccountId } from '@narval/policy-engine-shared' +import z from 'zod' +import { WalletType } from '../domain' +import { toChainAccountIdLowerCase } from '../utils' + +// Define Zod schema for ContractInformation +export const ContractInformation = z.object({ + factoryType: z.nativeEnum(WalletType).optional(), + assetType: z.nativeEnum(AssetType).optional() +}) +export type ContractInformation = z.infer + +// Define Zod schema for ContractRegistryInput +export const ContractRegistryInput = z.array( + z.object({ + contract: z.object({ + address: z + .string() + .transform((val) => val.toLowerCase()) + .pipe(addressSchema), + chainId: z.number().int().positive() + }), + assetType: z.nativeEnum(AssetType).optional(), + factoryType: z.nativeEnum(WalletType).optional() + }) +) +export type ContractRegistryInput = z.infer + +export class ContractRegistry extends Map { + constructor(input?: ContractRegistryInput) { + if (!input) { + super() + return + } + const validInput = ContractRegistryInput.parse(input) + const entries = validInput.map(({ contract, assetType, factoryType }): [ChainAccountId, ContractInformation] => { + const contractId = toChainAccountIdLowerCase(contract) + const contractInfo = ContractInformation.parse({ + assetType, + factoryType + }) + + return [contractId, contractInfo] + }) + + super(entries) + } +} diff --git a/packages/transaction-request-intent/src/lib/registry/transaction-registry.ts b/packages/transaction-request-intent/src/lib/registry/transaction-registry.ts new file mode 100644 index 000000000..c4cad2a09 --- /dev/null +++ b/packages/transaction-request-intent/src/lib/registry/transaction-registry.ts @@ -0,0 +1,40 @@ +import { ChainAccountId, toChainAccountId, TransactionRequest } from '@narval/policy-engine-shared' +import z from 'zod' +import { TransactionStatus } from '../domain' + +export const TransactionRegistryInput = z.array( + z.object({ + txRequest: TransactionRequest, + status: z.nativeEnum(TransactionStatus) + }) +) +export type TransactionRegistryInput = z.infer + +export const TransactionKey = z + .tuple([ChainAccountId, z.number().int().positive()]) + .transform(([chainAccountId, number]) => `${chainAccountId}-${number}` as `${ChainAccountId}-${number}`) +export type TransactionKey = z.infer + +export class TransactionRegistry extends Map { + constructor(input?: TransactionRegistryInput) { + if (!input) { + super() + return + } + const validInput = TransactionRegistryInput.parse(input) + + const entries = validInput.map(({ txRequest, status }): [TransactionKey, TransactionStatus] => { + const account = toChainAccountId({ + chainId: txRequest.chainId, + address: txRequest.from + }) + + const nonce = txRequest.nonce + const transactionKey = TransactionKey.parse([account, nonce]) + + return [transactionKey, status] + }) + + super(entries) + } +} diff --git a/packages/transaction-request-intent/src/lib/utils.ts b/packages/transaction-request-intent/src/lib/utils.ts index 8a29da9c8..ca07cb182 100644 --- a/packages/transaction-request-intent/src/lib/utils.ts +++ b/packages/transaction-request-intent/src/lib/utils.ts @@ -7,38 +7,28 @@ import { Eip712TypedData, Hex, Namespace, - TransactionRequest, assertHexString, - isAddress, - isChainAccountId, - isString, toAssetId, - toChainAccountId} from '@narval/policy-engine-shared' + toChainAccountId +} from '@narval/policy-engine-shared' import { SetOptional } from 'type-fest' import { Address, fromHex, presignMessagePrefix } from 'viem' import { - AssetTypeAndUnknown, - ChainRegistry, ContractCallInput, - ContractInformation, - ContractRegistry, - ContractRegistryInput, Intents, MessageInput, - Misc, NULL_METHOD_ID, NativeTransferInput, PERMIT2_DOMAIN, TransactionCategory, - TransactionKey, - TransactionRegistry, - TransactionStatus, WalletType } from './domain' import { DecoderError } from './error' import { Permit, Permit2, SignMessage, SignTypedData } from './intent.types' +import { ChainId, ChainRegistry } from './registry/chain-registry' +import { ContractInformation, ContractRegistry } from './registry/contract-registry' import { MethodsMapping, SUPPORTED_METHODS, SupportedMethodId } from './supported-methods' -import { isAssetType, isPermit, isPermit2, isSupportedMethodId } from './typeguards' +import { isPermit, isPermit2, isSupportedMethodId } from './typeguards' export const getMethodId = (data?: string): Hex => { if (!data || data === '0x') { @@ -64,74 +54,12 @@ export const getCategory = (methodId: Hex, to?: Hex | null, value?: Hex | null): return TransactionCategory.CONTRACT_INTERACTION } -export const buildContractRegistryEntry = ({ - chainId, - contractAddress, - assetType -}: { - chainId: number - contractAddress: string - assetType: string -}): { [key: ChainAccountId]: AssetTypeAndUnknown } => { - const registry: { [key: ChainAccountId]: AssetTypeAndUnknown } = {} - if (!isAddress(contractAddress) || !isAssetType(assetType)) { - throw new DecoderError({ message: 'Invalid contract registry entry', status: 400 }) - } - const key = buildContractKey(chainId, contractAddress as Address) - registry[key] = assetType - return registry -} - -export const buildContractRegistry = (input: ContractRegistryInput): ContractRegistry => { - const registry = new Map() - input.forEach(({ contract, assetType, factoryType }) => { - const information = { - assetType: assetType || Misc.UNKNOWN, - factoryType: factoryType || WalletType.UNKNOWN - } - if (isString(contract)) { - if (!isChainAccountId(contract)) { - throw new DecoderError({ - message: 'Contract registry key is not a valid Caip10', - status: 400, - context: { contract, input } - }) - } - registry.set(contract.toLowerCase(), information) - } else { - const key = buildContractKey(contract.chainId, contract.address) - registry.set(key, information) - } - }) - return registry -} - export const buildContractKey = ( chainId: number, contractAddress: Hex, namespace: Namespace = Namespace.EIP155 ): ChainAccountId => toChainAccountId({ namespace, chainId, address: contractAddress }) -export const checkContractRegistry = (registry: Record) => { - Object.keys(registry).forEach((key) => { - if (!isChainAccountId(key)) { - throw new DecoderError({ - message: 'Invalid contract registry key', - status: 400, - context: { key, value: registry[key] } - }) - } - if (!isAssetType(registry[key])) { - throw new DecoderError({ - message: 'Invalid contract registry value', - status: 400, - context: { key, value: registry[key] } - }) - } - }) - return true -} - export const contractTypeLookup = ( chainId: number, address?: Address, @@ -145,32 +73,6 @@ export const contractTypeLookup = ( return undefined } -export const buildTransactionKey = (txRequest: TransactionRequest): TransactionKey => { - if (!txRequest.nonce) { - throw new DecoderError({ message: 'nonce needed to build transaction key', status: 400 }) - } - const account = toChainAccountId({ - chainId: txRequest.chainId, - address: txRequest.from, - namespace: Namespace.EIP155 - }) - return `${account}-${txRequest.nonce}` -} - -export type TransactionRegistryInput = { - txRequest: TransactionRequest - status: TransactionStatus -}[] - -export const buildTransactionRegistry = (input: TransactionRegistryInput): TransactionRegistry => { - const registry = new Map() - input.forEach(({ txRequest, status }) => { - const key = buildTransactionKey(txRequest) - registry.set(key, status) - }) - return registry -} - export const decodeTypedData = (typedData: Eip712TypedData): SignTypedData => ({ type: Intents.SIGN_TYPED_DATA, typedData: Eip712TypedData.parse({ @@ -244,17 +146,6 @@ export const decodePermit2 = (typedData: Eip712TypedData): Permit2 | null => { } } -export const transactionLookup = ( - txRequest: TransactionRequest, - transactionRegistry?: TransactionRegistry -): TransactionStatus | undefined => { - const key: TransactionKey = buildTransactionKey(txRequest) - if (transactionRegistry) { - return transactionRegistry.get(key) - } - return undefined -} - export const getTransactionIntentType = ({ methodId, txRequest, @@ -328,7 +219,7 @@ export const checkCancelTransaction = (input: NativeTransferInput): Intents => { return Intents.TRANSFER_NATIVE } -export const nativeCaip19 = (chainId: number, registry: ChainRegistry): AssetId => { +export const nativeCaip19 = (chainId: ChainId, registry: ChainRegistry): AssetId => { const chain = registry.get(chainId) if (chain?.nativeSlip44 !== undefined) { return toAssetId({