-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
decoding erc20 and erc721 with tests
- Loading branch information
Showing
17 changed files
with
577 additions
and
11 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './lib/transaction-request-intent' | ||
export * from './lib/decoders' | ||
export * from './lib/intent' |
32 changes: 32 additions & 0 deletions
32
packages/transaction-request-intent/src/lib/__test__/unit/decoders.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { AssetTypeEnum, Intents } from '../../../utils/domain'; | ||
import { decodeIntent } from '../../../lib/decoders'; | ||
import { IntentRequest } from '../../../shared/types'; | ||
import { Erc20Methods } from '../../../utils/standard-functions/methodId'; | ||
|
||
jest.mock('viem', () => ({ | ||
decodeAbiParameters: jest.fn() | ||
.mockReturnValueOnce(['0x031d8C0cA142921c459bCB28104c0FF37928F9eD', BigInt('428406414311469998210669')]) | ||
})); | ||
|
||
describe('decodeIntent', () => { | ||
it('decodes ERC20 intent correctly', () => { | ||
const erc20Request: IntentRequest = { | ||
methodId: Erc20Methods.TRANSFER, | ||
assetType: AssetTypeEnum.ERC20, | ||
type: Intents.TRANSFER_ERC20, | ||
validatedFields: { | ||
data: '0xa9059cbb000000000000000000000000031d8c0ca142921c459bcb28104c0ff37928f9ed000000000000000000000000000000000000000000005ab7f55035d1e7b4fe6d', | ||
to: '0x000000000000000000000000000000000000000001', | ||
chainId: '1', | ||
}, | ||
}; | ||
|
||
const result = decodeIntent(erc20Request); | ||
|
||
expect(result).toEqual({ | ||
type: Intents.TRANSFER_ERC20, | ||
amount: '428406414311469998210669', | ||
token: 'eip155:1:0x000000000000000000000000000000000000000001', | ||
}); | ||
}); | ||
}); |
20 changes: 20 additions & 0 deletions
20
packages/transaction-request-intent/src/lib/__test__/unit/param-extractors.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
|
||
import { extractErc20TransferAmount } from '../../../utils/standard-functions/param-extractors'; | ||
|
||
jest.mock('viem', () => ({ | ||
decodeAbiParameters: jest.fn() | ||
.mockResolvedValueOnce([]) | ||
.mockReturnValueOnce(['0x031d8C0cA142921c459bCB28104c0FF37928F9eD', BigInt('428406414311469998210669')]) | ||
})); | ||
|
||
const invalidData = '0xInvalidData'; | ||
const validData = '0xa9059cbb000000000000000000000000031d8c0ca142921c459bcb28104c0ff37928f9ed000000000000000000000000000000000000000000005ab7f55035d1e7b4fe6d'; | ||
describe('extractErc20TransferAmount', () => { | ||
it('throws on incorrect data', () => { | ||
expect(() => extractErc20TransferAmount(invalidData)).toThrow('Malformed transaction request'); | ||
}); | ||
|
||
it('successfully extract amount on valid data', () => { | ||
expect(extractErc20TransferAmount(validData)).toEqual('428406414311469998210669'); | ||
}); | ||
}); |
7 changes: 0 additions & 7 deletions
7
packages/transaction-request-intent/src/lib/__test__/unit/transaction-request-intent.spec.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { Intent, TransferErc20, TransferErc721 } from '../../src/utils/intent.types'; | ||
import { encodeEoaAccountId, encodeEoaAssetId } from '../../src/utils/caip'; | ||
import { AssetTypeEnum, EipStandardEnum, Intents } from '../../src/utils/domain'; | ||
import { extractErc20Amount, extractErc721AssetId } from '../../src/utils/standard-functions/param-extractors'; | ||
import { IntentRequest } from '../../src/shared/types'; | ||
|
||
export const decodeErc721 = ({ | ||
data, | ||
methodId, | ||
chainId, | ||
assetType, | ||
to, | ||
}: { | ||
data: `0x${string}`, | ||
methodId: string, | ||
chainId: number, | ||
assetType: AssetTypeEnum, | ||
to: `0x${string}`, | ||
}) => { | ||
const intent: TransferErc721 = { | ||
type: Intents.TRANSFER_ERC721, | ||
nftId: encodeEoaAssetId({ | ||
eipStandard: EipStandardEnum.EIP155, | ||
assetType, | ||
chainId, | ||
evmAccountAddress: to, | ||
tokenId: extractErc721AssetId(data, methodId), | ||
}), | ||
nftContract: encodeEoaAccountId({ | ||
chainId, | ||
evmAccountAddress: to, | ||
}), | ||
} | ||
return intent; | ||
} | ||
|
||
export const decodeErc20 = ({ | ||
to, | ||
data, | ||
chainId, | ||
methodId, | ||
}: { | ||
to: `0x${string}`, | ||
data: `0x${string}`, | ||
chainId: number, | ||
methodId: string, | ||
}): TransferErc20 => { | ||
|
||
const intent: TransferErc20 = { | ||
type: Intents.TRANSFER_ERC20, | ||
amount: extractErc20Amount(data, methodId), | ||
token: encodeEoaAccountId({ | ||
chainId, | ||
evmAccountAddress: to, | ||
}), | ||
} | ||
|
||
return intent; | ||
}; | ||
|
||
export const decodeIntent = (request: IntentRequest): Intent => { | ||
const { methodId, type } = request; | ||
|
||
switch (type) { | ||
case Intents.TRANSFER_ERC20: | ||
return decodeErc20({ | ||
to: request.validatedFields.to, | ||
data: request.validatedFields.data, | ||
chainId: +request.validatedFields.chainId, | ||
methodId, | ||
}); | ||
case Intents.TRANSFER_ERC721: | ||
return decodeErc721({ | ||
assetType: request.assetType, | ||
to: request.validatedFields.to, | ||
data: request.validatedFields.data, | ||
chainId: +request.validatedFields.chainId, | ||
methodId, | ||
}); | ||
default: | ||
throw new Error('Unsupported intent'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { AssetTypeEnum, Intents, NATIVE_TRANSFER } from '../../src/utils/domain'; | ||
import { TransactionRequest } from '../../src/utils/transaction.type'; | ||
import { Intent } from '../../src/utils/intent.types'; | ||
import { decodeIntent } from './decoders'; | ||
import { Erc20TransferAbi, Erc721TransferAbi } from '../../src/utils/standard-functions/abis'; | ||
import { IntentRequest } from '../../src/shared/types'; | ||
|
||
const methodIdToAssetTypeMap: { [key: string]: AssetTypeEnum } = { | ||
...Object.entries(Erc20TransferAbi).reduce((acc, [key]) => ({ ...acc, [key]: AssetTypeEnum.ERC20 }), {}), | ||
...Object.entries(Erc721TransferAbi).reduce((acc, [key]) => ({ ...acc, [key]: AssetTypeEnum.ERC721 }), {}), | ||
[NATIVE_TRANSFER]: AssetTypeEnum.NATIVE, | ||
}; | ||
|
||
export const determineType = (methodId: string): AssetTypeEnum => { | ||
return methodIdToAssetTypeMap[methodId] || AssetTypeEnum.UNKNOWN; | ||
}; | ||
|
||
export const getIntentType = (assetType: AssetTypeEnum): Intents => { | ||
switch (assetType) { | ||
case AssetTypeEnum.ERC20: | ||
return Intents.TRANSFER_ERC20; | ||
case AssetTypeEnum.ERC721: | ||
return Intents.TRANSFER_ERC721; | ||
case AssetTypeEnum.ERC1155: | ||
return Intents.TRANSFER_ERC1155; | ||
case AssetTypeEnum.NATIVE: | ||
return Intents.TRANSFER_NATIVE; | ||
default: | ||
return Intents.CALL_CONTRACT; | ||
} | ||
} | ||
|
||
export const getMethodId = (data?: string): string => data ? data.slice(0, 10): NATIVE_TRANSFER; | ||
|
||
export const validateErc20Intent = (txRequest: TransactionRequest) => { | ||
const { data, to, chainId } = txRequest; | ||
if (!data || !to || !chainId) { | ||
throw new Error('Malformed Erc20 transaction request'); | ||
} | ||
return { data, to, chainId } | ||
} | ||
|
||
export const validateIntent = (txRequest: TransactionRequest): IntentRequest => { | ||
const { from, value, data, chainId } = txRequest; | ||
if (!from || !chainId) { | ||
throw new Error('Malformed transaction request: missing from or chainId'); | ||
} | ||
if (!value && !data) { | ||
throw new Error('Malformed transaction request: missing value and data'); | ||
} | ||
|
||
const methodId = getMethodId(data); | ||
const assetType = determineType(methodId); | ||
const type = getIntentType(assetType); | ||
|
||
switch (type) { | ||
case Intents.TRANSFER_ERC20: | ||
return { | ||
type, | ||
assetType, | ||
methodId, | ||
validatedFields: validateErc20Intent(txRequest), | ||
} | ||
default: | ||
throw new Error('Unsupported intent'); | ||
} | ||
}; | ||
|
||
export const decodeTransaction = (txRequest: TransactionRequest): Intent => { | ||
const request = validateIntent(txRequest); | ||
return decodeIntent(request); | ||
}; |
3 changes: 0 additions & 3 deletions
3
packages/transaction-request-intent/src/lib/transaction-request-intent.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { AssetTypeEnum, Intents } from '../utils/domain'; | ||
|
||
export type TransferIntent = { | ||
data: `0x${string}`; | ||
to: `0x${string}`; | ||
chainId: string; | ||
} | ||
|
||
export type IntentRequest = { | ||
methodId: string; | ||
assetType: AssetTypeEnum; | ||
} & ( | ||
| { | ||
type: Intents.TRANSFER_ERC20; | ||
validatedFields: TransferIntent; | ||
} | ||
| { | ||
type: Intents.TRANSFER_ERC721; | ||
validatedFields: TransferIntent; | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Address } from 'viem'; | ||
import { AssetTypeEnum, EipStandardEnum } from './domain'; | ||
import { SetOptional } from 'type-fest'; | ||
import { AccountId, AssetId } from 'caip'; | ||
|
||
export type Caip10 = string & { readonly brand: unique symbol }; | ||
|
||
export type Caip19 = string & { readonly brand: unique symbol }; | ||
|
||
export type Caip10Standards = { | ||
chainId: number | 'eoa'; | ||
evmAccountAddress: Address; | ||
eipStandard: EipStandardEnum; | ||
}; | ||
|
||
export type Caip19Standards = Caip10Standards & { | ||
tokenId: string; | ||
assetType: AssetTypeEnum; | ||
}; | ||
|
||
export const encodeEoaAccountId = ({ | ||
chainId, | ||
evmAccountAddress, | ||
eipStandard = EipStandardEnum.EIP155, | ||
}: SetOptional<Caip10Standards, 'eipStandard'>): Caip10 => | ||
new AccountId({ | ||
chainId: { namespace: eipStandard, reference: chainId.toString() }, | ||
address: evmAccountAddress.toLowerCase(), | ||
}) | ||
.toString() | ||
.toLowerCase() as Caip10; | ||
|
||
export const encodeEoaAssetId = ({ | ||
chainId, | ||
evmAccountAddress, | ||
eipStandard = EipStandardEnum.EIP155, | ||
tokenId, | ||
assetType, | ||
}: Caip19Standards): Caip19 => | ||
new AssetId({ | ||
chainId: { namespace: eipStandard, reference: chainId.toString() }, | ||
assetName: { | ||
namespace: assetType, | ||
reference: evmAccountAddress, | ||
}, | ||
tokenId, | ||
}) | ||
.toString() | ||
.toLowerCase() as Caip19; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
export enum BlockchainActions { | ||
SIGN_TRANSACTION = 'signTransaction', | ||
SIGN_RAW = 'signRaw', | ||
SIGN_MESSAGE = 'signMessage', | ||
SIGN_TYPED_DATA = 'signTypedData', | ||
} | ||
|
||
export enum PolicyManagementActions { | ||
SET_POLICY_RULES = 'setPolicyRules', | ||
} | ||
|
||
export type Actions = BlockchainActions | PolicyManagementActions; | ||
|
||
export enum Intents { | ||
TRANSFER_NATIVE = 'transferNative', | ||
TRANSFER_ERC20 = 'transferErc20', | ||
TRANSFER_ERC721 = 'transferErc721', | ||
TRANSFER_ERC1155 = 'transferErc1155', | ||
CALL_CONTRACT = 'callContract', | ||
} | ||
|
||
export const NATIVE_TRANSFER = 'nativeTransfer'; | ||
|
||
// TODO: Move below in a folder shared with other apps, these should be shared within the whole project | ||
export enum AssetTypeEnum { | ||
ERC1155 = 'erc1155', | ||
ERC20 = 'erc20', | ||
ERC721 = 'erc721', | ||
NATIVE = 'native', | ||
UNKNOWN = 'unknown', | ||
} | ||
|
||
export enum EipStandardEnum { | ||
EIP155 = 'eip155', | ||
} |
Oops, something went wrong.