Skip to content

Commit

Permalink
storageRefAccount
Browse files Browse the repository at this point in the history
  • Loading branch information
joe-p committed Jul 24, 2023
1 parent fe65b4f commit 7cf20f9
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 8 deletions.
55 changes: 48 additions & 7 deletions src/lib/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ export default class Compiler {
accessors?: (ts.Expression | string)[]
storageExpression?: ts.CallExpression
storageKeyFrame?: string
storageAccountFrame?: string
}
} = {};

Expand Down Expand Up @@ -511,7 +512,7 @@ export default class Compiler {
},
},
local: {
get: (node: ts.CallExpression, storageKeyFrame?: string) => {
get: (node: ts.CallExpression, storageKeyFrame?: string, storageAccountFrame?: string) => {
if (!ts.isPropertyAccessExpression(node.expression)) throw new Error();
if (!ts.isPropertyAccessExpression(node.expression.expression)) throw new Error();
const name = node.expression.expression.name.getText();
Expand All @@ -520,7 +521,11 @@ export default class Compiler {
valueType, keyType, key, prefix,
} = this.storageProps[name];

this.processNode(node.arguments[0]);
if (storageAccountFrame) {
this.pushVoid(node.expression, `frame_dig ${this.frame[storageAccountFrame].index} // ${storageAccountFrame}`);
} else {
this.processNode(node.arguments[0]);
}

if (key) {
this.pushVoid(node.expression, `byte "${key}"`);
Expand All @@ -541,7 +546,7 @@ export default class Compiler {
this.push(node.expression, 'app_local_get', valueType);
if (valueType !== StackType.bytes) this.checkDecoding(node, valueType);
},
set: (node: ts.CallExpression, storageKeyFrame?: string) => {
set: (node: ts.CallExpression, storageKeyFrame?: string, storageAccountFrame?: string) => {
if (!ts.isPropertyAccessExpression(node.expression)) throw new Error();
if (!ts.isPropertyAccessExpression(node.expression.expression)) throw new Error();
const name = node.expression.expression.name.getText();
Expand All @@ -550,7 +555,11 @@ export default class Compiler {
valueType, keyType, key, prefix,
} = this.storageProps[name];

this.processNode(node.arguments[0]);
if (storageAccountFrame) {
this.pushVoid(node.expression, `frame_dig ${this.frame[storageAccountFrame].index} // ${storageAccountFrame}`);
} else {
this.processNode(node.arguments[0]);
}

if (key) {
this.pushVoid(node.expression, `byte "${key}"`);
Expand All @@ -573,7 +582,12 @@ export default class Compiler {
if (valueType !== StackType.bytes) {
this.checkEncoding(node.arguments[key ? 1 : 2], this.lastType);
}
} else this.pushVoid(node.expression, 'uncover 2'); // Used when updating storage array
} else {
this.pushVoid(node.expression, 'uncover 2'); // Used when updating storage array
if (valueType !== StackType.bytes) {
this.checkEncoding(node, valueType);
}
}

this.push(node.expression, 'app_local_put', valueType);
},
Expand Down Expand Up @@ -1859,7 +1873,14 @@ export default class Compiler {
node: ts.Node,
inputName: string,
load: boolean,
): {accessors: (ts.Expression | string)[], name: string, type: 'frame' | 'storage', storageExpression?: ts.CallExpression, storageKeyFrame?: string} {
): {
accessors: (ts.Expression | string)[],
name: string,
type: 'frame' | 'storage',
storageExpression?: ts.CallExpression,
storageKeyFrame?: string
storageAccountFrame?: string
} {
let name = inputName;
let currentFrame = this.frame[inputName];
let type: 'frame' | 'storage' = 'frame';
Expand Down Expand Up @@ -1889,13 +1910,15 @@ export default class Compiler {
accessors: accessors.reverse().flat(),
storageExpression,
storageKeyFrame: currentFrame.storageKeyFrame,
storageAccountFrame: currentFrame.storageAccountFrame,
};
}

if (currentFrame.storageExpression !== undefined) {
this.storageFunctions[this.storageProps[name].type].get(
currentFrame.storageExpression,
currentFrame.storageKeyFrame,
currentFrame.storageAccountFrame,
);
} else {
this.push(
Expand Down Expand Up @@ -1928,6 +1951,7 @@ export default class Compiler {
this.storageFunctions[type].set(
processedFrame.storageExpression,
processedFrame.storageKeyFrame,
processedFrame.storageAccountFrame,
);
}
}
Expand Down Expand Up @@ -2738,7 +2762,24 @@ export default class Compiler {
this.frame[name].storageKeyFrame = keyFrameName;
}

// TODO: Save local account
if (storageProp.type === 'local') {
const accountNode = storageExpression.arguments[0];
const accountFrameName = `storage account//${name}`;

this.addSourceComment(node, true);
this.processNode(accountNode);

this.pushVoid(accountNode, `frame_bury ${this.frameIndex} // ${accountFrameName}`);

this.frame[accountFrameName] = {
index: this.frameIndex,
type: StackType.uint64,
};

this.frameIndex -= 1;

this.frame[name].storageAccountFrame = accountFrameName;
}
}

private processVariableDeclarator(node: ts.VariableDeclaration) {
Expand Down
8 changes: 7 additions & 1 deletion tests/abi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ async function runMethod(
};

try {
if (name.includes('Storage')) {
if (name.includes('Storage') || name.includes('RefAccount')) {
await appClient.fundAppAccount({
amount: algokit.microAlgos(127400),
sendParams: { suppressLog: true },
Expand Down Expand Up @@ -685,4 +685,10 @@ describe('ABI', function () {

expect(await runMethod(appClient, 'storageRefKey')).toEqual(4n);
});

test.concurrent('storageRefAccount', async () => {
const { appClient } = await compileAndCreate('storageRefAccount');

expect(await runMethod(appClient, 'storageRefAccount')).toEqual(4n);
});
});
17 changes: 17 additions & 0 deletions tests/contracts/abi.algo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -957,3 +957,20 @@ class ABITestStorageRefKey extends Contract {
return this.gMap.get(0)[1];
}
}

class ABITestStorageRefAccount extends Contract {
lMap = new LocalStateMap<uint64, uint64[]>();

@handle.optIn
storageRefAccount(): uint64 {
let addr = this.txn.sender;
this.lMap.set(addr, 0, [1, 2, 3]);
const r = this.lMap.get(addr, 0);

addr = globals.zeroAddress;

r[1] = 4;

return this.lMap.get(this.txn.sender, 0)[1];
}
}
15 changes: 15 additions & 0 deletions tests/contracts/artifacts/ABITestStorageRefAccount.abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "ABITestStorageRefAccount",
"desc": "",
"methods": [
{
"name": "storageRefAccount",
"args": [],
"desc": "",
"returns": {
"type": "uint64",
"desc": ""
}
}
]
}
134 changes: 134 additions & 0 deletions tests/contracts/artifacts/ABITestStorageRefAccount.approval.teal
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#pragma version 8
b main

abi_route_storageRefAccount:
txn OnCompletion
int OptIn
==
txn ApplicationID
int 0
!=
&&
assert
byte 0x; dup
callsub storageRefAccount
int 1
return

storageRefAccount:
proto 2 0

// tests/contracts/abi.algo.ts:966
// addr = this.txn.sender
txn Sender
frame_bury -1 // addr: address

// tests/contracts/abi.algo.ts:967
// this.lMap.set(addr, 0, [1, 2, 3])
frame_dig -1 // addr: address
int 0
itob
int 1
itob
int 2
itob
concat
int 3
itob
concat
dup
len
int 8
/
itob
extract 6 2
swap
concat
app_local_put

// tests/contracts/abi.algo.ts:968
// r = this.lMap.get(addr, 0)
frame_dig -1 // addr: address
frame_bury -2 // storage account//r

// tests/contracts/abi.algo.ts:970
// addr = globals.zeroAddress
global ZeroAddress
frame_bury -1 // addr: address

// tests/contracts/abi.algo.ts:972
// r[1] = 4
frame_dig -2 // storage account//r
int 0
itob
app_local_get
extract 2 0
store 0 // full array
int 0 // initial offset
int 1
int 8
* // acc * typeLength
+
load 0 // full array
swap
int 4
itob
replace3
frame_dig -2 // storage account//r
int 0
itob
uncover 2
dup
len
int 8
/
itob
extract 6 2
swap
concat
app_local_put

// tests/contracts/abi.algo.ts:974
// return this.lMap.get(this.txn.sender, 0)[1];
txn Sender
int 0
itob
app_local_get
extract 2 0
store 0 // full array
int 0 // initial offset
int 1
int 8
* // acc * typeLength
+
load 0 // full array
swap
int 8
extract3
btoi
itob
byte 0x151f7c75
swap
concat
log
retsub

main:
txn NumAppArgs
bnz route_abi

// default createApplication
txn ApplicationID
int 0
==
txn OnCompletion
int NoOp
==
&&
return

route_abi:
method "storageRefAccount()uint64"
txna ApplicationArgs 0
match abi_route_storageRefAccount
err
3 changes: 3 additions & 0 deletions tests/contracts/artifacts/ABITestStorageRefAccount.clear.teal
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma version 8
int 1
return
56 changes: 56 additions & 0 deletions tests/contracts/artifacts/ABITestStorageRefAccount.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"hints": {
"storageRefAccount()uint64": {
"call_config": {
"optIn": "CALL"
}
}
},
"bare_call_config": {
"no_op": "CREATE"
},
"schema": {
"local": {
"declared": {
"lMap": {
"type": "bytes",
"key": "lMap"
}
},
"reserved": {}
},
"global": {
"declared": {},
"reserved": {}
}
},
"state": {
"global": {
"num_byte_slices": 0,
"num_uints": 0
},
"local": {
"num_byte_slices": 1,
"num_uints": 0
}
},
"source": {
"approval": "I3ByYWdtYSB2ZXJzaW9uIDgKCWIgbWFpbgoKYWJpX3JvdXRlX3N0b3JhZ2VSZWZBY2NvdW50OgoJdHhuIE9uQ29tcGxldGlvbgoJaW50IE9wdEluCgk9PQoJdHhuIEFwcGxpY2F0aW9uSUQKCWludCAwCgkhPQoJJiYKCWFzc2VydAoJYnl0ZSAweDsgZHVwCgljYWxsc3ViIHN0b3JhZ2VSZWZBY2NvdW50CglpbnQgMQoJcmV0dXJuCgpzdG9yYWdlUmVmQWNjb3VudDoKCXByb3RvIDIgMAoKCS8vIHRlc3RzL2NvbnRyYWN0cy9hYmkuYWxnby50czo5NjYKCS8vIGFkZHIgPSB0aGlzLnR4bi5zZW5kZXIKCXR4biBTZW5kZXIKCWZyYW1lX2J1cnkgLTEgLy8gYWRkcjogYWRkcmVzcwoKCS8vIHRlc3RzL2NvbnRyYWN0cy9hYmkuYWxnby50czo5NjcKCS8vIHRoaXMubE1hcC5zZXQoYWRkciwgMCwgWzEsIDIsIDNdKQoJZnJhbWVfZGlnIC0xIC8vIGFkZHI6IGFkZHJlc3MKCWludCAwCglpdG9iCglpbnQgMQoJaXRvYgoJaW50IDIKCWl0b2IKCWNvbmNhdAoJaW50IDMKCWl0b2IKCWNvbmNhdAoJZHVwCglsZW4KCWludCA4CgkvCglpdG9iCglleHRyYWN0IDYgMgoJc3dhcAoJY29uY2F0CglhcHBfbG9jYWxfcHV0CgoJLy8gdGVzdHMvY29udHJhY3RzL2FiaS5hbGdvLnRzOjk2OAoJLy8gciA9IHRoaXMubE1hcC5nZXQoYWRkciwgMCkKCWZyYW1lX2RpZyAtMSAvLyBhZGRyOiBhZGRyZXNzCglmcmFtZV9idXJ5IC0yIC8vIHN0b3JhZ2UgYWNjb3VudC8vcgoKCS8vIHRlc3RzL2NvbnRyYWN0cy9hYmkuYWxnby50czo5NzAKCS8vIGFkZHIgPSBnbG9iYWxzLnplcm9BZGRyZXNzCglnbG9iYWwgWmVyb0FkZHJlc3MKCWZyYW1lX2J1cnkgLTEgLy8gYWRkcjogYWRkcmVzcwoKCS8vIHRlc3RzL2NvbnRyYWN0cy9hYmkuYWxnby50czo5NzIKCS8vIHJbMV0gPSA0CglmcmFtZV9kaWcgLTIgLy8gc3RvcmFnZSBhY2NvdW50Ly9yCglpbnQgMAoJaXRvYgoJYXBwX2xvY2FsX2dldAoJZXh0cmFjdCAyIDAKCXN0b3JlIDAgLy8gZnVsbCBhcnJheQoJaW50IDAgLy8gaW5pdGlhbCBvZmZzZXQKCWludCAxCglpbnQgOAoJKiAvLyBhY2MgKiB0eXBlTGVuZ3RoCgkrCglsb2FkIDAgLy8gZnVsbCBhcnJheQoJc3dhcAoJaW50IDQKCWl0b2IKCXJlcGxhY2UzCglmcmFtZV9kaWcgLTIgLy8gc3RvcmFnZSBhY2NvdW50Ly9yCglpbnQgMAoJaXRvYgoJdW5jb3ZlciAyCglkdXAKCWxlbgoJaW50IDgKCS8KCWl0b2IKCWV4dHJhY3QgNiAyCglzd2FwCgljb25jYXQKCWFwcF9sb2NhbF9wdXQKCgkvLyB0ZXN0cy9jb250cmFjdHMvYWJpLmFsZ28udHM6OTc0CgkvLyByZXR1cm4gdGhpcy5sTWFwLmdldCh0aGlzLnR4bi5zZW5kZXIsIDApWzFdOwoJdHhuIFNlbmRlcgoJaW50IDAKCWl0b2IKCWFwcF9sb2NhbF9nZXQKCWV4dHJhY3QgMiAwCglzdG9yZSAwIC8vIGZ1bGwgYXJyYXkKCWludCAwIC8vIGluaXRpYWwgb2Zmc2V0CglpbnQgMQoJaW50IDgKCSogLy8gYWNjICogdHlwZUxlbmd0aAoJKwoJbG9hZCAwIC8vIGZ1bGwgYXJyYXkKCXN3YXAKCWludCA4CglleHRyYWN0MwoJYnRvaQoJaXRvYgoJYnl0ZSAweDE1MWY3Yzc1Cglzd2FwCgljb25jYXQKCWxvZwoJcmV0c3ViCgptYWluOgoJdHhuIE51bUFwcEFyZ3MKCWJueiByb3V0ZV9hYmkKCgkvLyBkZWZhdWx0IGNyZWF0ZUFwcGxpY2F0aW9uCgl0eG4gQXBwbGljYXRpb25JRAoJaW50IDAKCT09Cgl0eG4gT25Db21wbGV0aW9uCglpbnQgTm9PcAoJPT0KCSYmCglyZXR1cm4KCnJvdXRlX2FiaToKCW1ldGhvZCAic3RvcmFnZVJlZkFjY291bnQoKXVpbnQ2NCIKCXR4bmEgQXBwbGljYXRpb25BcmdzIDAKCW1hdGNoIGFiaV9yb3V0ZV9zdG9yYWdlUmVmQWNjb3VudAoJZXJy",
"clear": "I3ByYWdtYSB2ZXJzaW9uIDgKaW50IDEKcmV0dXJu"
},
"contract": {
"name": "ABITestStorageRefAccount",
"desc": "",
"methods": [
{
"name": "storageRefAccount",
"args": [],
"desc": "",
"returns": {
"type": "uint64",
"desc": ""
}
}
]
}
}

0 comments on commit 7cf20f9

Please sign in to comment.