@nori-zk/mina-token-bridge
Version:
A Mina zk-program contract allowing users to mint tokens on Nori Bridge.
199 lines • 8.61 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import { AccountUpdate, assert, Bool, Field, method, Permissions, Poseidon, Provable, PublicKey, State, state, TokenContract, UInt64, VerificationKey, } from 'o1js';
import { NoriStorageInterface } from './NoriStorageInterface.js';
import { FungibleToken } from './TokenBase.js';
import { EthDepositProgramProofType } from './e2ePrerequisites.js';
import { ProvableEcdsaSigPresentation } from './credentialAttestation.js';
export class NoriTokenController extends TokenContract {
constructor() {
super(...arguments);
this.adminPublicKey = State();
this.tokenBaseAddress = State();
this.ethProcessorAddress = State();
this.storageVKHash = State();
this.mintLock = State();
}
async deploy(props) {
await super.deploy(props);
this.adminPublicKey.set(props.adminPublicKey);
this.tokenBaseAddress.set(props.tokenBaseAddress);
this.ethProcessorAddress.set(props.ethProcessorAddress);
this.storageVKHash.set(props.storageVKHash);
this.mintLock.set(Bool(true));
this.account.permissions.set({
...Permissions.default(),
setVerificationKey: Permissions.VerificationKey.impossibleDuringCurrentVersion(),
setPermissions: Permissions.impossible(),
editState: Permissions.proof(),
send: Permissions.proof(),
});
}
approveBase(forest) {
throw Error('block updates');
}
async setUpStorage(user, vk) {
let tokenAccUpdate = AccountUpdate.createSigned(user, this.deriveTokenId());
// TODO: what if someone sent token to this address before?
tokenAccUpdate.account.isNew.requireEquals(Bool(true));
// could use the idea of vkMap from latest standard
const storageVKHash = this.storageVKHash.getAndRequireEquals();
storageVKHash.assertEquals(vk.hash);
tokenAccUpdate.body.update.verificationKey = {
isSome: Bool(true),
value: vk,
};
tokenAccUpdate.body.update.permissions = {
isSome: Bool(true),
value: {
...Permissions.default(),
editState: Permissions.proof(),
// VK upgradability here?
setVerificationKey: Permissions.VerificationKey.impossibleDuringCurrentVersion(),
setPermissions: Permissions.proof(), //imposible?
},
};
AccountUpdate.setValue(tokenAccUpdate.update.appState[0], //NoriStorageInterface.userKeyHash
Poseidon.hash(user.toFields()));
AccountUpdate.setValue(tokenAccUpdate.update.appState[1], //NoriStorageInterface.mintedSoFar
Field(0));
}
/** Update the verification key.
*/
async updateVerificationKey(vk) {
await this.ensureAdminSignature();
this.account.verificationKey.set(vk);
}
async ensureAdminSignature() {
const admin = await Provable.witnessAsync(PublicKey, async () => {
let pk = await this.adminPublicKey.fetch();
assert(pk !== undefined, 'could not fetch admin public key');
return pk;
});
this.adminPublicKey.requireEquals(admin);
return AccountUpdate.createSigned(admin);
}
async noriMint(ethDepositProof, presentationProof) {
const userAddress = this.sender.getUnconstrained(); //TODO make user pass signature due to limit of AU
const tokenAddress = this.tokenBaseAddress.getAndRequireEquals();
let { claims, outputClaim } = presentationProof.verify({
publicKey: this.address,
tokenId: this.tokenId,
methodName: 'verifyPresentation', // TODO RENAME
});
Provable.asProver(() => {
Provable.log('ethDepositProof.publicOutput.attestationHash', 'outputClaim.messageHash', ethDepositProof.publicOutput.attestationHash, outputClaim.messageHash);
});
ethDepositProof.publicOutput.attestationHash.assertEquals(outputClaim.messageHash);
//TODO when add ethProcessor
// assert ethDepositProof.publicOutput.storageDepositRoot;
const controllerTokenId = this.deriveTokenId();
let storage = new NoriStorageInterface(userAddress, controllerTokenId);
storage.account.isNew.requireEquals(Bool(false)); // that somehow allows to getState without index out of bounds
storage.userKeyHash
.getAndRequireEquals()
.assertEquals(Poseidon.hash(userAddress.toFields()), ' userKeyHash mismatch');
// LHS e1 -> s1 -> 1 RHS s1 + mpt + da .... 1 mint
// LHS e1 -> s2 -> 1(2) RHS s2 + mpr + da .... want to mint 2.... total locked 1 claim (1).... cannot claim 2 because in this run we only deposited 1
const amountToMint = await storage.increaseMintedAmount(ethDepositProof.publicOutput.totalLocked); // TODO test mint amount is sane.
Provable.log(amountToMint, 'amount to mint');
// Here we have only one destination there is only m1.....
let token = new FungibleToken(tokenAddress);
this.mintLock.set(Bool(false));
Provable.asProver(() => {
console.log('UInt64.Unsafe.fromField(amountToMint)', UInt64.Unsafe.fromField(amountToMint).toBigInt());
});
// Mint!
await token.mint(userAddress, UInt64.Unsafe.fromField(amountToMint));
}
async canMint(_accountUpdate) {
this.mintLock.requireEquals(Bool(false));
this.mintLock.set(Bool(true));
return Bool(true);
}
async canChangeAdmin(_admin) {
await this.ensureAdminSignature();
return Bool(true);
}
async canPause() {
await this.ensureAdminSignature();
return Bool(true);
}
async canResume() {
await this.ensureAdminSignature();
return Bool(true);
}
async canChangeVerificationKey(_vk) {
await this.ensureAdminSignature();
return Bool(true);
}
}
__decorate([
state(PublicKey),
__metadata("design:type", Object)
], NoriTokenController.prototype, "adminPublicKey", void 0);
__decorate([
state(PublicKey),
__metadata("design:type", Object)
], NoriTokenController.prototype, "tokenBaseAddress", void 0);
__decorate([
state(PublicKey),
__metadata("design:type", Object)
], NoriTokenController.prototype, "ethProcessorAddress", void 0);
__decorate([
state(Field),
__metadata("design:type", Object)
], NoriTokenController.prototype, "storageVKHash", void 0);
__decorate([
state(Bool),
__metadata("design:type", Object)
], NoriTokenController.prototype, "mintLock", void 0);
__decorate([
method,
__metadata("design:type", Function),
__metadata("design:paramtypes", [PublicKey, VerificationKey]),
__metadata("design:returntype", Promise)
], NoriTokenController.prototype, "setUpStorage", null);
__decorate([
method,
__metadata("design:type", Function),
__metadata("design:paramtypes", [VerificationKey]),
__metadata("design:returntype", Promise)
], NoriTokenController.prototype, "updateVerificationKey", null);
__decorate([
method,
__metadata("design:type", Function),
__metadata("design:paramtypes", [EthDepositProgramProofType,
ProvableEcdsaSigPresentation]),
__metadata("design:returntype", Promise)
], NoriTokenController.prototype, "noriMint", null);
__decorate([
method.returns(Bool),
__metadata("design:type", Function),
__metadata("design:paramtypes", [AccountUpdate]),
__metadata("design:returntype", Promise)
], NoriTokenController.prototype, "canMint", null);
__decorate([
method.returns(Bool),
__metadata("design:type", Function),
__metadata("design:paramtypes", [PublicKey]),
__metadata("design:returntype", Promise)
], NoriTokenController.prototype, "canChangeAdmin", null);
__decorate([
method.returns(Bool),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], NoriTokenController.prototype, "canPause", null);
__decorate([
method.returns(Bool),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], NoriTokenController.prototype, "canResume", null);
__decorate([
method.returns(Bool),
__metadata("design:type", Function),
__metadata("design:paramtypes", [VerificationKey]),
__metadata("design:returntype", Promise)
], NoriTokenController.prototype, "canChangeVerificationKey", null);
//# sourceMappingURL=NoriTokenController.js.map