-
Notifications
You must be signed in to change notification settings - Fork 143
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
Token transfers between zkApps #300
Comments
Current ApproachTo serve as a point of discussion and documentation, we outline the current approach being used to implement token transfers between zkApps. We want to be able to support zkApp's to transfer tokens with proofs instead of just signatures. The reason for this is to support one zkApp that is holding a custom token to transfer it without requiring a signature from the token owner zkApp. Currently, there is no way to support this since the zkApp that wants to transfer a custom token must get authorization from the token owner zkApp, and requiring a signature does not work from a programmatic standpoint. For this sort of interaction, we need to enable token transfers to be authorized by proofs from the token owner since To solve this issue, we present a new class to be added to SnarkyJS called Using a Callback to generate proofsThe only way to generate proofs for zkApps is via a With this in mind, we can declare a new class called Code ExampleLet's assume we have a parent token contract called class TokenContract extends SmartContract {
...
@method sendTokens(
senderAddress: PublicKey,
receiverAddress: PublicKey,
callback: Experimental.Callback<any>
) {
let senderParty = Experimental.partyFromCallback(this, callback);
let amount = UInt64.from(1_000);
let negativeAmount = Int64.fromObject(senderParty.body.balanceChange);
negativeAmount.assertEquals(Int64.from(amount).neg());
let tokenId = this.experimental.token.id;
senderParty.body.tokenId.assertEquals(tokenId);
senderParty.body.publicKey.assertEquals(senderAddress);
let receiverParty = Experimental.createChildParty(
this.self,
receiverAddress,
{ caller: tokenId, tokenId }
);
receiverParty.balance.addInPlace(amount);
}
} You will notice that the class ZkAppB extends SmartContract {
// A method to send custom tokens derived from `TokenContract`
@method authorizeSend() {
let amount = UInt64.from(1_000);
this.balance.subInPlace(amount);
}
}
...
let tx = await Local.transaction(feePayer, () => {
let authorizeSendingCallback = new Experimental.Callback(
zkAppC,
'authorizeSend',
[]
);
// we call the token contract with the callback
tokenZkApp.sendTokens(zkAppCAddress, tokenAccount1, authorizeSendingCallback);
});
await tx.prove(); This callback will be run by the parent zkApp and then authorize the callback with proof. The child zkApp can then call the parent zkApp method Deploying a child with a custom token IDAnother change to be added is to make a tokenId an optional parameter to be passed into a let tokenZkApp = new TokenContract(tokenZkAppAddress);
let tokenId = tokenZkApp.token().id;
let zkAppB = new ZkAppB(zkAppBAddress, tokenId); One requirement that is imposed on the child zkApp when they deploy with a custom token id is that they must get authorization from the parent zkApp. Concretely, this means we cannot deploy as normal if we specify a different custom token id since we would need permissions from the parent zkApp just like regular custom token interactions. To get around this, we will add a new type of class TokenContract extends SmartContract {
...
@method tokenDeploy(deployer: PrivateKey) {
// Authorize a child zkApp using TokenContract's custom token id to be deployed
super.token().deploy({ deployer });
}
}
...
let tx = await Local.transaction(feePayer, () => {
Party.fundNewAccount(feePayer);
tokenZkApp.tokenDeploy(zkAppBKey);
});
tx.sign([zkAppBKey]);
tx.send(); As a final thing to note, this means that each instantiation of a zkApp with a different token id must be deployed for each unique token id. The same zkApp code can be re-used but must be initialized with a different token id and redeployed. |
Am I correct in reading that this is essentially a workaround for 'proving is async, but circuits are not'? If so, it seems like we should probably just fix that. |
@MartinMinkov @mrmr1993 I think the description is a bit inaccurate which might lead to confusion. We can already create proofs by the token owner / token contract, what we're missing is a way to add a proof authorization from the sender - a zkApp that wants to send tokens. We need a callback for that since the token contract wants to inspect its child parties, and the sender-zkApp needs to have freedom in what exact party it creates. So, for the token contract to support generic sender-zkApps, it has to be given a way to witness a party whose precise structure it doesn't know, and make assertions about it. That's what the callback is for (it's creating the sender-zkApp party, to be witnessed and inpected by the token contract) |
The
token()
APIsburn
andsend
should enable authorizing token sending with a proofInitial discussion here: #273 (comment)
The text was updated successfully, but these errors were encountered: