Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: l2 metadata decoding #7702

Merged
merged 11 commits into from
Nov 16, 2023
27 changes: 15 additions & 12 deletions packages/shared/lib/core/layer-2/classes/special-stream.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ export class SpecialStream extends WriteStream {
}

export class ReadSpecialStream extends ReadStream {
readUInt64SpecialEncoding(name: string): number | bigint {
const [value] = size64Decode(() => this.readUInt8(name))
return value
readUInt64SpecialEncodingWithError(name: string): [bigint, Error | null] {
return size64Decode(() => this.readUInt8(name))
}

readUInt32SpecialEncoding(name: string): number | bigint {
Expand Down Expand Up @@ -137,33 +136,37 @@ function size64Encode(n: bigint): Buffer {
}

// Adapted from WASP golang implementation https://github.com/iotaledger/wasp/blob/7f880a7983d24d0dcd225e994d67b29741b318bc/packages/util/rwutil/convert.go#L76
function size64Decode(readByte: () => number): [number, null | Error] {
function size64Decode(readByte: () => number): [bigint, null | Error] {
let byte = readByte()

if (!byte) {
return [BigInt(0), new Error('no more bytes')]
}

if (byte < 0x80) {
return [byte, null]
return [BigInt(byte), null]
}

let value = byte & 0x7f
let value = BigInt(byte) & BigInt(0x7f)

for (let shift = 7; shift < 63; shift += 7) {
byte = readByte()
if (!byte) {
return [0, null]
return [BigInt(0), new Error('no more bytes')]
}
if (byte < 0x80) {
return [Number(value | (byte << shift)), null]
return [value | (BigInt(byte) << BigInt(shift)), null]
}
value |= (byte & 0x7f) << shift
value |= (BigInt(byte) & BigInt(0x7f)) << BigInt(shift)
}

byte = readByte()
if (!byte) {
return [0, null]
return [BigInt(0), new Error('no more bytes')]
}
if (byte > 0x01) {
return [0, new Error('size64 overflow')]
return [BigInt(0), new Error('size64 overflow')]
}

return [value | (byte << 63), new Error('Unexpected end of data')]
return [value | (BigInt(byte) << BigInt(63)), null]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ describe('Function: parseLayer2MetadataForTransfer.ts', () => {
senderContract: '0x0',
targetContract: 'Accounts',
contractFunction: 'transferAllowanceTo',
gasBudget: '10001',
ethereumAddress:
'0x42f7da9bdb55b3ec87e5ac1a1e6d88e16768663fde5eca3429eb6f579cc538acb82a77d6f89dae4611b81eac279fbf96d322001f',
gasBudget: '10000',
ethereumAddress: '0xb82a77d6f89dae4611b81eac279fbf96d322001f',
baseTokens: '900000000',
nativeTokens: [],
nfts: [],
Expand All @@ -21,6 +20,24 @@ describe('Function: parseLayer2MetadataForTransfer.ts', () => {
expect(parsedMetadata).toEqual(expected)
})

it('should correctly parse metadata with long base token', () => {
const metadata =
'0x00025e4b3ca1e3f423914e010161350342f7da9bdb55b3ec87e5ac1a1e6d88e16768663fde5eca3429eb6f579cc538acb82a77d6f89dae4611b81eac279fbf96d322001f80ff9f94a58d1d'
const metadataByteArray = Converter.hexToBytes(metadata)
const expected = {
senderContract: '0x0',
targetContract: 'Accounts',
contractFunction: 'transferAllowanceTo',
gasBudget: '10000',
ethereumAddress: '0xb82a77d6f89dae4611b81eac279fbf96d322001f',
baseTokens: '999999999999',
nativeTokens: [],
nfts: [],
}
const parsedMetadata = parseLayer2MetadataForTransfer(metadataByteArray)
expect(parsedMetadata).toEqual(expected)
})

it('should correctly parse metadata with native tokens', () => {
const metadata =
'0x00025e4b3ca1e3f423914e010161350342f7da9bdb55b3ec87e5ac1a1e6d88e16768663fde5eca3429eb6f579cc538acb82a77d6f89dae4611b81eac279fbf96d322001f4001086ac702fcfdc37b437e7ebb7a87d8acfb875be6b1ae3823bc61aa7896b852a6d5010000000001fa'
Expand All @@ -29,9 +46,8 @@ describe('Function: parseLayer2MetadataForTransfer.ts', () => {
senderContract: '0x0',
targetContract: 'Accounts',
contractFunction: 'transferAllowanceTo',
gasBudget: '10001',
ethereumAddress:
'0x42f7da9bdb55b3ec87e5ac1a1e6d88e16768663fde5eca3429eb6f579cc538acb82a77d6f89dae4611b81eac279fbf96d322001f',
gasBudget: '10000',
ethereumAddress: '0xb82a77d6f89dae4611b81eac279fbf96d322001f',
baseTokens: '0',
nativeTokens: [
{
Expand All @@ -53,9 +69,8 @@ describe('Function: parseLayer2MetadataForTransfer.ts', () => {
senderContract: '0x0',
targetContract: 'Accounts',
contractFunction: 'transferAllowanceTo',
gasBudget: '26345',
ethereumAddress:
'0x42f7da9bdb55b3ec87e5ac1a1e6d88e16768663fde5eca3429eb6f579cc538acb82a77d6f89dae4611b81eac279fbf96d322001f',
gasBudget: '26344',
ethereumAddress: '0xb82a77d6f89dae4611b81eac279fbf96d322001f',
baseTokens: '0',
nativeTokens: [],
nfts: ['0xbf5b7cd4e8ac582e246c25b6a89b4ab4ef0646d3291aa03d9a5313154b714a06'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { ILayer2TransferAllowanceMetadata } from '../interfaces'

export type Layer2Metadata = Omit<ILayer2TransferAllowanceMetadata, 'baseTokens' | 'nativeTokens' | 'nfts'>
export type Layer2Metadata = Omit<ILayer2TransferAllowanceMetadata, 'nativeTokens' | 'nfts'>
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ export function parseLayer2MetadataForTransfer(metadata: Uint8Array): ILayer2Tra
const senderContract = readStream.readUInt8('senderContract')
const targetContract = readStream.readUInt32('targetContract')
const contractFunction = readStream.readUInt32('contractFunction')
const gasBudget = readStream.readUInt64SpecialEncoding('gasBudget')
const smartContractParameters = parseSmartContractParameters(readStream)
const ethereumAddress = '0x' + smartContractParameters['a'].substring(4)
const gasBudget = parseGasBudget(readStream)
const ethereumAddress = parseEvmAddressFromAgentId(readStream)
const allowance = parseAssetAllowance(readStream)

return {
Expand All @@ -27,6 +26,19 @@ export function parseLayer2MetadataForTransfer(metadata: Uint8Array): ILayer2Tra
}
}

function parseGasBudget(readStream: ReadSpecialStream): bigint | number {
const [value, error] = readStream.readUInt64SpecialEncodingWithError('gasBudget')
if (!error) {
return value - BigInt(1)
}
return value
}

function parseEvmAddressFromAgentId(readStream: ReadSpecialStream): string {
const smartContractParameters = parseSmartContractParameters(readStream)
return '0x' + smartContractParameters['a'].slice(-40)
}

function parseSmartContractParameters(readStream: ReadSpecialStream): Record<string, string> {
const smartContractParametersAmount = readStream.readUInt32SpecialEncoding('parametersLength')
const smartContractParameters: Record<string, string> = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export async function generateSingleBasicActivity(
const id = outputId || transactionId

const output = wrappedOutput.output as BasicOutput
const amount = getAmountFromOutput(output)

const isShimmerClaiming = isShimmerClaimingTransaction(transactionId, get(activeProfileId))

Expand All @@ -44,13 +45,15 @@ export async function generateSingleBasicActivity(
const asyncData = await getAsyncDataFromOutput(output, outputId, claimingData, account)

const { parsedLayer2Metadata, destinationNetwork } = getLayer2ActivityInformation(metadata, sendingInfo)
const layer2Allowance = Number(parsedLayer2Metadata?.baseTokens ?? '0')
const gasBudget = Number(parsedLayer2Metadata?.gasBudget ?? '0')
const gasFee = layer2Allowance > 0 ? amount - layer2Allowance : 0

let { storageDeposit, giftedStorageDeposit } = await getStorageDepositFromOutput(account, output)
giftedStorageDeposit = action === ActivityAction.Burn ? 0 : giftedStorageDeposit
giftedStorageDeposit = gasBudget === 0 ? giftedStorageDeposit : 0

const baseTokenAmount = getAmountFromOutput(output) - storageDeposit - gasBudget
const baseTokenAmount = amount - storageDeposit - gasFee

const nativeToken = await getNativeTokenFromOutput(output)
const assetId = fallbackAssetId ?? nativeToken?.id ?? getCoinType()
Expand Down
Loading