Skip to content

Commit

Permalink
Support token approval
Browse files Browse the repository at this point in the history
  • Loading branch information
h0ngcha0 committed Apr 18, 2024
1 parent 45afe74 commit 7d0dfa9
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 26 deletions.
16 changes: 9 additions & 7 deletions .project.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@
"infos": {
"Add": {
"sourceFile": "add/add.ral",
"sourceCodeHash": "ff387b4feef2fd71654165255dd2eb872b8e7366f976419693f9124ed60a7ab2",
"bytecodeDebugPatch": "=10-2+50=2-2+70=2-2+79=63+77e010a=1+1646450726976617465=152",
"codeHashDebug": "9a4cf22e444db365cc62468b22db303401e6942409e193055e7f5f0318b389ca",
"sourceCodeHash": "b3a4696a12e8cbb2cb5fb1ebe18000beb0e67e9ff7194300b01f9cff8862701c",
"bytecodeDebugPatch": "=10-3+5=1-2=2-2+70=3+8=1+0a1=63+77e010a=1+1646450726976617465=232",
"codeHashDebug": "d43cf958572ed347683a41906781126fa8f76e9890a96a3d607d2ecff91b25ce",
"warnings": [
"Found unused variables in Add: add2.addS, add2.address, add2.array2",
"The return values of the function \"Add.copyCreateSubContract\" are not used. If this is intentional, consider using anonymous variables to suppress this warning.",
"The return values of the function \"Add.copyCreateSubContract\" are not used. If this is intentional, consider using anonymous variables to suppress this warning.",
"No external caller check for function \"Add.createSubContract\". Please use \"checkCaller!(...)\" in the function or its callees, or disable it with \"@using(checkExternalCaller = false)\".",
"No external caller check for function \"Add.add\". Please use \"checkCaller!(...)\" in the function or its callees, or disable it with \"@using(checkExternalCaller = false)\".",
"No external caller check for function \"Add.add2\". Please use \"checkCaller!(...)\" in the function or its callees, or disable it with \"@using(checkExternalCaller = false)\"."
"No external caller check for function \"Add.add2\". Please use \"checkCaller!(...)\" in the function or its callees, or disable it with \"@using(checkExternalCaller = false)\".",
"No external caller check for function \"Add.createSubContractAndTransfer\". Please use \"checkCaller!(...)\" in the function or its callees, or disable it with \"@using(checkExternalCaller = false)\"."
]
},
"AddMain": {
"sourceFile": "add/add.ral",
"sourceCodeHash": "ff387b4feef2fd71654165255dd2eb872b8e7366f976419693f9124ed60a7ab2",
"sourceCodeHash": "b3a4696a12e8cbb2cb5fb1ebe18000beb0e67e9ff7194300b01f9cff8862701c",
"bytecodeDebugPatch": "",
"codeHashDebug": "",
"warnings": [
Expand All @@ -33,14 +35,14 @@
},
"AddStruct1": {
"sourceFile": "add/add.ral",
"sourceCodeHash": "ff387b4feef2fd71654165255dd2eb872b8e7366f976419693f9124ed60a7ab2",
"sourceCodeHash": "b3a4696a12e8cbb2cb5fb1ebe18000beb0e67e9ff7194300b01f9cff8862701c",
"bytecodeDebugPatch": "",
"codeHashDebug": "",
"warnings": []
},
"AddStruct2": {
"sourceFile": "add/add.ral",
"sourceCodeHash": "ff387b4feef2fd71654165255dd2eb872b8e7366f976419693f9124ed60a7ab2",
"sourceCodeHash": "b3a4696a12e8cbb2cb5fb1ebe18000beb0e67e9ff7194300b01f9cff8862701c",
"bytecodeDebugPatch": "",
"codeHashDebug": "",
"warnings": []
Expand Down
29 changes: 27 additions & 2 deletions artifacts/add/Add.ral.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"version": "v2.11.0",
"name": "Add",
"bytecode": "02050d1a40434063406c0100021002041600160100020201000c0c02041600160100020200000202021605160016015f06160016015fa00016002a16012aa100a000160016010e0dce000100020103040600101300641600130164170517041603d1a21601160216041605c1180102010100021600b0",
"codeHash": "cb9bcf12cd42167de9cd08cbb974d03263bf8fd12fae62340a72029f35497b04",
"bytecode": "02060d1a40434063408b40940100021002041600160100020201000c0c02041600160100020200000202021605160016015f06160016015fa00016002a16012aa100a000160016010e0dce000100020103040c00101300641600130164170517041603d1a21601160216041605c118010104060014130064160013016417051704160316021340c8ac1603d1a21601160216041605c1180102010100021600b0",
"codeHash": "6fa2e126a8361f8bfba25ce9c0dc57924427ec46bb99720ddab30c1105123ec5",
"fieldsSig": {
"names": [
"sub",
Expand Down Expand Up @@ -130,6 +130,31 @@
],
"returnTypes": []
},
{
"name": "createSubContractAndTransfer",
"usePreapprovedAssets": true,
"useAssetsInContract": true,
"isPublic": true,
"paramNames": [
"a",
"path",
"subContractId",
"payer"
],
"paramTypes": [
"U256",
"ByteVec",
"ByteVec",
"Address"
],
"paramIsMutable": [
false,
false,
false,
false
],
"returnTypes": []
},
{
"name": "destroy",
"usePreapprovedAssets": false,
Expand Down
2 changes: 1 addition & 1 deletion artifacts/add/DestroyAdd.ral.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "v2.11.0",
"name": "DestroyAdd",
"bytecodeTemplate": "01010300000005{1}0d0c{0}0104",
"bytecodeTemplate": "01010300000005{1}0d0c{0}0105",
"fieldsSig": {
"names": [
"add",
Expand Down
53 changes: 51 additions & 2 deletions artifacts/ts/Add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ export namespace AddTypes {
}>;
result: CallContractResult<null>;
};
createSubContractAndTransfer: {
params: CallContractParams<{
a: bigint;
path: HexString;
subContractId: HexString;
payer: Address;
}>;
result: CallContractResult<null>;
};
destroy: {
params: CallContractParams<{ caller: Address }>;
result: CallContractResult<null>;
Expand Down Expand Up @@ -116,6 +125,15 @@ export namespace AddTypes {
}>;
result: SignExecuteScriptTxResult;
};
createSubContractAndTransfer: {
params: SignExecuteContractMethodParams<{
a: bigint;
path: HexString;
subContractId: HexString;
payer: Address;
}>;
result: SignExecuteScriptTxResult;
};
destroy: {
params: SignExecuteContractMethodParams<{ caller: Address }>;
result: SignExecuteScriptTxResult;
Expand Down Expand Up @@ -176,6 +194,14 @@ class Factory extends ContractFactory<AddInstance, AddTypes.Fields> {
): Promise<TestContractResultWithoutMaps<null>> => {
return testMethod(this, "createSubContract", params);
},
createSubContractAndTransfer: async (
params: TestContractParamsWithoutMaps<
AddTypes.Fields,
{ a: bigint; path: HexString; subContractId: HexString; payer: Address }
>
): Promise<TestContractResultWithoutMaps<null>> => {
return testMethod(this, "createSubContractAndTransfer", params);
},
destroy: async (
params: TestContractParamsWithoutMaps<
AddTypes.Fields,
Expand All @@ -191,8 +217,8 @@ class Factory extends ContractFactory<AddInstance, AddTypes.Fields> {
export const Add = new Factory(
Contract.fromJson(
AddContractJson,
"=10-2+50=2-2+70=2-2+79=63+77e010a=1+1646450726976617465=152",
"9a4cf22e444db365cc62468b22db303401e6942409e193055e7f5f0318b389ca",
"=10-3+5=1-2=2-2+70=3+8=1+0a1=63+77e010a=1+1646450726976617465=232",
"d43cf958572ed347683a41906781126fa8f76e9890a96a3d607d2ecff91b25ce",
AllStructs
)
);
Expand Down Expand Up @@ -266,6 +292,17 @@ export class AddInstance extends ContractInstance {
getContractByCodeHash
);
},
createSubContractAndTransfer: async (
params: AddTypes.CallMethodParams<"createSubContractAndTransfer">
): Promise<AddTypes.CallMethodResult<"createSubContractAndTransfer">> => {
return callMethod(
Add,
this,
"createSubContractAndTransfer",
params,
getContractByCodeHash
);
},
destroy: async (
params: AddTypes.CallMethodParams<"destroy">
): Promise<AddTypes.CallMethodResult<"destroy">> => {
Expand All @@ -291,6 +328,18 @@ export class AddInstance extends ContractInstance {
): Promise<AddTypes.SignExecuteMethodResult<"createSubContract">> => {
return signExecuteMethod(Add, this, "createSubContract", params);
},
createSubContractAndTransfer: async (
params: AddTypes.SignExecuteMethodParams<"createSubContractAndTransfer">
): Promise<
AddTypes.SignExecuteMethodResult<"createSubContractAndTransfer">
> => {
return signExecuteMethod(
Add,
this,
"createSubContractAndTransfer",
params
);
},
destroy: async (
params: AddTypes.SignExecuteMethodParams<"destroy">
): Promise<AddTypes.SignExecuteMethodResult<"destroy">> => {
Expand Down
7 changes: 7 additions & 0 deletions contracts/add/add.ral
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ Contract Add(sub: Sub, mut result : U256) {
copyCreateSubContract!{payer -> ALPH: minimalContractDeposit!()}(path, subContractId, immFields, mutFields)
}

@using(preapprovedAssets = true, assetsInContract = true)
pub fn createSubContractAndTransfer(a: U256, path: ByteVec, subContractId: ByteVec, payer: Address) -> () {
let (immFields, mutFields) = Sub.encodeFields!(a)
transferTokenToSelf!(payer, subContractId, 200)
copyCreateSubContract!{payer -> ALPH: minimalContractDeposit!()}(path, subContractId, immFields, mutFields)
}

@using(checkExternalCaller = false, assetsInContract = true)
pub fn destroy(caller: Address) -> () {
destroySelf!(caller)
Expand Down
60 changes: 50 additions & 10 deletions packages/web3/src/contract/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ import {
ByteVecEq,
Assert,
StoreMutFieldByIndex,
DestroySelf
DestroySelf,
byteStringCodec
} from '../codec'

const crypto = new WebCrypto()
Expand Down Expand Up @@ -2327,13 +2328,9 @@ function encodeBytecodeTemplate(

const [templateVarStoreLocalInstrs, templateVarsLength] = getTemplateVarStoreLocalInstrs(functionSig, structs)

const approveInstrs: string[] = []
if (approve?.attoAlphAmount) {
const approvedAttoAlphAmount = encodeU256Const(BigInt(approve.attoAlphAmount))
approveInstrs.push('b4') // callerAddress
approveInstrs.push(approvedAttoAlphAmount)
approveInstrs.push('a2') // ApproveAlph
}
const approveAlphInstrs: string[] = getApproveAlphInstrs(approve?.attoAlphAmount)
const approveTokensInstrs: string[] = getApproveTokensInstrs(approve?.tokens)
const callerInstrs: string[] = getCallAddressInstrs(approveAlphInstrs.length / 2 + approveTokensInstrs.length / 3)

// -1 because the first template var is the contract
const functionArgsNum = encodeU256Const(BigInt(templateVarsLength - 1))
Expand All @@ -2352,7 +2349,9 @@ function encodeBytecodeTemplate(
const externalCallInstr = '01' + methodIndex.toString(16).padStart(2, '0')
const numberOfInstrs = compactSignedIntCodec
.encodeI32(
approveInstrs.length +
callerInstrs.length +
approveAlphInstrs.length +
approveTokensInstrs.length +
templateVarStoreLocalInstrs.length +
templateVarLoadLocalInstrs.length +
functionReturnTypesLength +
Expand All @@ -2368,7 +2367,9 @@ function encodeBytecodeTemplate(
localsLength +
returnsLength +
numberOfInstrs +
approveInstrs.join('') +
callerInstrs.join('') +
approveAlphInstrs.join('') +
approveTokensInstrs.join('') +
templateVarStoreLocalInstrs.join('') +
templateVarLoadLocalInstrs.join('') +
functionArgsNum +
Expand All @@ -2379,6 +2380,45 @@ function encodeBytecodeTemplate(
)
}

function getApproveAlphInstrs(attoAlphAmount?: Number256): string[] {
const approveAlphInstrs: string[] = []
if (attoAlphAmount) {
const approvedAttoAlphAmount = encodeU256Const(BigInt(attoAlphAmount))
approveAlphInstrs.push(approvedAttoAlphAmount)
approveAlphInstrs.push('a2') // ApproveAlph
}

return approveAlphInstrs
}

function getApproveTokensInstrs(tokens?: Token[]): string[] {
const approveTokensInstrs: string[] = []
if (tokens) {
tokens.forEach((token) => {
const tokenId = byteStringCodec.encodeBuffer(Buffer.from(token.id, 'hex'))
const tokenAmount = encodeU256Const(BigInt(token.amount))
approveTokensInstrs.push('1440' + tokenId.toString('hex'))
approveTokensInstrs.push(tokenAmount)
approveTokensInstrs.push('a3') // ApproveToken
})
}

return approveTokensInstrs
}

function getCallAddressInstrs(approveAssetsNum: number): string[] {
const callerInstrs: string[] = []
if (approveAssetsNum > 0) {
callerInstrs.push('b4') // callerAddress

if (approveAssetsNum > 1) {
callerInstrs.push(...new Array(approveAssetsNum - 1).fill('7a')) // dup
}
}

return callerInstrs
}

function getTemplateVarStoreLocalInstrs(functionSig: FunctionSig, structs: Struct[]): [string[], number] {
let templateVarIndex = 1 // Start from 1 since first one is always the contract id
let localsLength = 0
Expand Down
16 changes: 12 additions & 4 deletions test/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,15 +578,19 @@ describe('contract', function () {
})

it('should test sign execute method with approved assets', async () => {
const sub = await Sub.deploy(signer, { initialFields: { result: 0n } })
const signerAddress = (await signer.getSelectedAccount()).address
const sub = await Sub.deploy(signer, {
initialFields: { result: 0n },
issueTokenAmount: 300n,
issueTokenTo: signerAddress
})
const add = (await Add.deploy(signer, { initialFields: { sub: sub.contractInstance.contractId, result: 0n } }))
.contractInstance
const signerAddress = (await signer.getSelectedAccount()).address
const provider = web3.getCurrentNodeProvider()

const state = await provider.contracts.getContractsAddressState(add.address)
expect(state).toBeDefined()
await add.transaction.createSubContract({
await add.transaction.createSubContractAndTransfer({
args: {
a: 1n,
path: stringToHex('test-path'),
Expand All @@ -595,7 +599,11 @@ describe('contract', function () {
},
signer,
attoAlphAmount: ONE_ALPH * 2n,
approve: { attoAlphAmount: ONE_ALPH }
tokens: [{ id: sub.contractInstance.contractId, amount: 200n }],
approve: {
attoAlphAmount: ONE_ALPH,
tokens: [{ id: sub.contractInstance.contractId, amount: 200n }]
}
})
})
})

0 comments on commit 7d0dfa9

Please sign in to comment.