Skip to content

Commit

Permalink
Merge pull request #450 from alephium/dapp-tx-builder
Browse files Browse the repository at this point in the history
Introduce DappTransactionBuilder
  • Loading branch information
polarker authored Nov 21, 2024
2 parents 97aa3ed + 0fb2509 commit 69cb409
Show file tree
Hide file tree
Showing 7 changed files with 517 additions and 53 deletions.
16 changes: 16 additions & 0 deletions packages/web3/src/codec/instr-codec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,20 @@ describe('Encode & decode instrs', function () {
expect(instrCodec.decode(encoded)).toEqual(instr)
})
})

it('should check the numeric bounds', () => {
expect(() => instr.toU256(0n)).not.toThrow()
expect(() => instr.toU256(1n)).not.toThrow()
expect(() => instr.toU256((1n << 256n) - 1n)).not.toThrow()
expect(() => instr.toU256(-1n)).toThrow('Invalid u256')
expect(() => instr.toU256(1n << 256n)).toThrow('Invalid u256')

expect(() => instr.toI256(0n)).not.toThrow()
expect(() => instr.toI256(-1n)).not.toThrow()
expect(() => instr.toI256(1n)).not.toThrow()
expect(() => instr.toI256(-(1n << 255n))).not.toThrow()
expect(() => instr.toI256((1n << 255n) - 1n)).not.toThrow()
expect(() => instr.toI256(1n << 255n)).toThrow('Invalid i256')
expect(() => instr.toI256(-(1n << 255n) - 1n)).toThrow('Invalid i256')
})
})
57 changes: 56 additions & 1 deletion packages/web3/src/codec/instr-codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ArrayCodec } from './array-codec'
import { i256Codec, u256Codec, i32Codec } from './compact-int-codec'
import { ByteString, byteStringCodec, byteStringsCodec } from './bytestring-codec'
import { LockupScript, lockupScriptCodec } from './lockup-script-codec'
import { byteCodec, Codec } from './codec'
import { assert, byteCodec, Codec } from './codec'
import { intAs4BytesCodec } from './int-as-4bytes-codec'
import { Reader } from './reader'
export type Instr =
Expand Down Expand Up @@ -1263,3 +1263,58 @@ export class InstrCodec extends Codec<Instr> {
}
export const instrCodec = new InstrCodec()
export const instrsCodec = new ArrayCodec<Instr>(instrCodec)

function checkU256(number: bigint) {
if (number < 0n || number >= 2n ** 256n) {
throw new Error(`Invalid u256 number: ${number}`)
}
}

export function toU256(number: bigint) {
checkU256(number)
switch (number) {
case 0n:
return U256Const0
case 1n:
return U256Const1
case 2n:
return U256Const2
case 3n:
return U256Const3
case 4n:
return U256Const4
case 5n:
return U256Const5
default:
return U256Const(number)
}
}

function checkI256(number: bigint) {
const upperBound = 2n ** 255n
if (number < -upperBound || number >= upperBound) {
throw new Error(`Invalid i256 number: ${number}`)
}
}

export function toI256(number: bigint) {
checkI256(number)
switch (number) {
case 0n:
return I256Const0
case 1n:
return I256Const1
case 2n:
return I256Const2
case 3n:
return I256Const3
case 4n:
return I256Const4
case 5n:
return I256Const5
case -1n:
return I256ConstN1
default:
return I256Const(number)
}
}
157 changes: 157 additions & 0 deletions packages/web3/src/contract/dapp-tx-builder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
Copyright 2018 - 2022 The Alephium Authors
This file is part of the alephium project.
The library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the library. If not, see <http://www.gnu.org/licenses/>.
*/

import { randomContractAddress, randomContractId, testAddress } from '@alephium/web3-test'
import { DappTransactionBuilder, genArgs } from './dapp-tx-builder'
import {
AddressConst,
BytesConst,
ConstFalse,
ConstTrue,
I256Const,
I256Const1,
I256ConstN1,
U256Const1,
U256Const2
} from '../codec'
import { base58ToBytes, hexToBinUnsafe } from '../utils'
import { lockupScriptCodec } from '../codec/lockup-script-codec'
import { ALPH_TOKEN_ID, ONE_ALPH } from '../constants'

describe('dapp-tx-builder', function () {
it('should gen code for args', () => {
expect(genArgs(['1i', '2u', '-1', '2'])).toEqual([I256Const1, U256Const2, I256ConstN1, U256Const2])

expect(genArgs([false, 1n, -1n, '0011', testAddress])).toEqual([
ConstFalse,
U256Const1,
I256ConstN1,
BytesConst(hexToBinUnsafe('0011')),
AddressConst(lockupScriptCodec.decode(base58ToBytes(testAddress)))
])

expect(genArgs([false, [1n, 2n], ['0011', '2233']])).toEqual([
ConstFalse,
U256Const1,
U256Const2,
BytesConst(hexToBinUnsafe('0011')),
BytesConst(hexToBinUnsafe('2233'))
])

expect(genArgs([true, { array0: [1n, 2n], array1: [-1n, -2n] }])).toEqual([
ConstTrue,
U256Const1,
U256Const2,
I256ConstN1,
I256Const(-2n)
])

expect(() => genArgs(['1234a'])).toThrow('Invalid number')
expect(() => genArgs([2n ** 256n])).toThrow('Invalid u256 number')
expect(() => genArgs([-(2n ** 256n)])).toThrow('Invalid i256 number')
expect(() => genArgs([new Map<string, bigint>()])).toThrow('Map cannot be used as a function argument')
})

it('should build dapp txs', () => {
expect(() => new DappTransactionBuilder(randomContractAddress())).toThrow('Invalid caller address')
expect(() => new DappTransactionBuilder('Il')).toThrow('Invalid caller address')

const builder = new DappTransactionBuilder(testAddress)
expect(() =>
builder.callContract({
contractAddress: testAddress,
methodIndex: 0,
args: []
})
).toThrow('Invalid contract address')

expect(() =>
builder.callContract({
contractAddress: randomContractAddress(),
methodIndex: -1,
args: []
})
).toThrow('Invalid method index')

const commonParams = { contractAddress: randomContractAddress(), methodIndex: 0, args: [] }
expect(() =>
builder.callContract({
...commonParams,
tokens: [{ id: 'invalid id', amount: 1n }]
})
).toThrow('Invalid token id')

expect(() =>
builder.callContract({
...commonParams,
tokens: [{ id: randomContractId().slice(0, 60), amount: 1n }]
})
).toThrow('Invalid token id')

expect(() =>
builder.callContract({
...commonParams,
tokens: [{ id: ALPH_TOKEN_ID, amount: -1n }]
})
).toThrow('Invalid token amount')

const result0 = builder.callContract({ ...commonParams }).getResult()
expect(result0.attoAlphAmount).toEqual(undefined)
expect(result0.tokens).toEqual([])

const tokenId0 = randomContractId()
const tokenId1 = randomContractId()
const result1 = builder
.callContract({
...commonParams,
attoAlphAmount: ONE_ALPH,
tokens: [
{ id: ALPH_TOKEN_ID, amount: ONE_ALPH },
{ id: tokenId0, amount: ONE_ALPH },
{ id: tokenId1, amount: 0n }
]
})
.getResult()
expect(result1.attoAlphAmount).toEqual(ONE_ALPH * 2n)
expect(result1.tokens).toEqual([{ id: tokenId0, amount: ONE_ALPH }])

const result2 = builder
.callContract({
...commonParams,
attoAlphAmount: 0n,
tokens: [
{ id: tokenId0, amount: 0n },
{ id: tokenId1, amount: ONE_ALPH }
]
})
.callContract({
...commonParams,
attoAlphAmount: ONE_ALPH,
tokens: [
{ id: tokenId0, amount: ONE_ALPH },
{ id: tokenId1, amount: ONE_ALPH }
]
})
.getResult()
expect(result2.attoAlphAmount).toEqual(ONE_ALPH)
expect(result2.tokens).toEqual([
{ id: tokenId1, amount: ONE_ALPH * 2n },
{ id: tokenId0, amount: ONE_ALPH }
])
})
})
Loading

0 comments on commit 69cb409

Please sign in to comment.