@nori-zk/mina-token-bridge
Version:
Nori ethereum state settelment and nETH token bridge zkApp
232 lines • 13.6 kB
JavaScript
var _NoriTokenBridgeSubmitter_instances, _NoriTokenBridgeSubmitter_zkApp, _NoriTokenBridgeSubmitter_senderPrivateKey, _NoriTokenBridgeSubmitter_possibleTokenBridgePrivateKey, _NoriTokenBridgeSubmitter_network, _NoriTokenBridgeSubmitter_txFee, _NoriTokenBridgeSubmitter_testMode, _NoriTokenBridgeSubmitter_noriTokenBridgeVerificationKey_get;
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
import 'dotenv/config';
import { AccountUpdate, Bool, Field, Mina, PrivateKey, PublicKey, UInt8, fetchAccount, } from 'o1js';
import { Logger } from 'esm-iso-logger';
import { NoriTokenBridge } from './NoriTokenBridge.js';
import { NoriStorageInterface } from './NoriStorageInterface.js';
import { FungibleToken } from './TokenBase.js';
import { EthInput, decodeConsensusMptProof, Bytes32FieldPair, NodeProofLeft, compileAndOptionallyVerifyContracts, bridgeHeadNoriSP1HeliosProgramPi0, proofConversionSP1ToPlonkPO2, } from '@nori-zk/o1js-zk-utils';
import { cacheFactory } from '@nori-zk/o1js-zk-utils/node';
import { FrC, } from '@nori-zk/proof-conversion/min';
import { noriTokenBridgeVkHash } from './integrity/NoriTokenBridge.VkHash.js';
import { noriStorageInterfaceVkHash } from './integrity/NoriStorageInterface.VkHash.js';
import { fungibleTokenVkHash } from './integrity/FungibleToken.VkHash.js';
const logger = new Logger('NoriTokenBridgeSubmitter');
export class NoriTokenBridgeSubmitter {
constructor(cache = undefined) {
_NoriTokenBridgeSubmitter_instances.add(this);
this.cache = cache;
_NoriTokenBridgeSubmitter_zkApp.set(this, void 0);
_NoriTokenBridgeSubmitter_senderPrivateKey.set(this, void 0);
_NoriTokenBridgeSubmitter_possibleTokenBridgePrivateKey.set(this, void 0);
_NoriTokenBridgeSubmitter_network.set(this, void 0);
_NoriTokenBridgeSubmitter_txFee.set(this, void 0);
_NoriTokenBridgeSubmitter_testMode.set(this, void 0);
void __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_testMode, "f");
logger.info(`NoriTokenBridgeSubmitter constructor called.`);
const errors = [];
const possibleSenderPrivateKeyBase58 = process.env
.MINA_SENDER_PRIVATE_KEY;
const possibleNetwork = process.env.MINA_NETWORK;
const possibleNetworkUrl = process.env.MINA_RPC_NETWORK_URL;
const possibleArchiveUrl = process.env.MINA_ARCHIVE_RPC_URL;
if (!possibleSenderPrivateKeyBase58)
errors.push('MINA_SENDER_PRIVATE_KEY is required');
if (!possibleNetwork) {
errors.push('MINA_NETWORK is required');
}
else if (!['devnet', 'mainnet', 'lightnet'].includes(possibleNetwork)) {
errors.push(`MINA_NETWORK must be one of: devnet, mainnet, lightnet (got "${possibleNetwork}")`);
}
else {
__classPrivateFieldSet(this, _NoriTokenBridgeSubmitter_network, possibleNetwork, "f");
}
if (!possibleNetworkUrl)
errors.push('MINA_RPC_NETWORK_URL is required');
if (!possibleArchiveUrl)
errors.push('MINA_ARCHIVE_RPC_URL is required');
const isLightnet = possibleNetwork === 'lightnet';
const possibleTokenBridgePrivateKeyBase58 = process.env
.NORI_MINA_TOKEN_BRIDGE_PRIVATE_KEY;
const possibleTokenBridgeAddressBase58 = process.env
.NORI_MINA_TOKEN_BRIDGE_ADDRESS;
if (isLightnet) {
if (!possibleTokenBridgePrivateKeyBase58)
errors.push('NORI_MINA_TOKEN_BRIDGE_PRIVATE_KEY is required in lightnet mode');
}
else {
if (!possibleTokenBridgeAddressBase58)
errors.push('NORI_MINA_TOKEN_BRIDGE_ADDRESS is required');
}
if (errors.length > 0) {
throw `Configuration errors:\n- ${errors.join('\n- ')}`;
}
__classPrivateFieldSet(this, _NoriTokenBridgeSubmitter_senderPrivateKey, PrivateKey.fromBase58(possibleSenderPrivateKeyBase58), "f");
__classPrivateFieldSet(this, _NoriTokenBridgeSubmitter_txFee, Number(process.env.MINA_TX_FEE || 0.1) * 1e9, "f");
__classPrivateFieldSet(this, _NoriTokenBridgeSubmitter_testMode, isLightnet, "f");
this.minaRPCNetworkUrl = possibleNetworkUrl;
this.minaArchiveRPCUrl = possibleArchiveUrl;
if (isLightnet) {
__classPrivateFieldSet(this, _NoriTokenBridgeSubmitter_possibleTokenBridgePrivateKey, PrivateKey.fromBase58(possibleTokenBridgePrivateKeyBase58), "f");
__classPrivateFieldSet(this, _NoriTokenBridgeSubmitter_zkApp, new NoriTokenBridge(__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_possibleTokenBridgePrivateKey, "f").toPublicKey()), "f");
}
else {
__classPrivateFieldSet(this, _NoriTokenBridgeSubmitter_possibleTokenBridgePrivateKey, undefined, "f");
__classPrivateFieldSet(this, _NoriTokenBridgeSubmitter_zkApp, new NoriTokenBridge(PublicKey.fromBase58(possibleTokenBridgeAddressBase58)), "f");
}
logger.log('Loaded constants from: .env');
}
async networkSetUp() {
logger.log(`Setting up ${__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_network, "f")} network with RPC endpoint: '${this.minaRPCNetworkUrl}' and archive endpoint: '${this.minaArchiveRPCUrl}'.`);
const networkId = __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_network, "f") === 'mainnet' ? 'mainnet' : 'testnet';
const Network = Mina.Network({
networkId,
mina: this.minaRPCNetworkUrl,
archive: this.minaArchiveRPCUrl,
});
Mina.setActiveInstance(Network);
logger.log('Finished Mina network setup.');
}
async compileContracts() {
const fileSystemCache = this.cache
? await cacheFactory(this.cache)
: undefined;
const { NoriStorageInterfaceVerificationKey, FungibleTokenVerificationKey, NoriTokenBridgeVerificationKey, } = await compileAndOptionallyVerifyContracts(logger, [
{
name: 'NoriStorageInterface',
program: NoriStorageInterface,
integrityHash: noriStorageInterfaceVkHash,
},
{
name: 'FungibleToken',
program: FungibleToken,
integrityHash: fungibleTokenVkHash,
},
{
name: 'NoriTokenBridge',
program: NoriTokenBridge,
integrityHash: noriTokenBridgeVkHash,
},
], fileSystemCache);
Object.defineProperty(this, 'noriStorageInterfaceVerificationKey', {
value: NoriStorageInterfaceVerificationKey,
writable: false,
configurable: false,
enumerable: true,
});
Object.defineProperty(this, 'noriTokenBridgeVerificationKey', {
value: NoriTokenBridgeVerificationKey,
writable: false,
configurable: false,
enumerable: true,
});
void FungibleTokenVerificationKey;
}
async deployContract(storeHash, ethTokenBridgeAddress, genesisRoot) {
if (__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_network, "f") !== 'lightnet') {
throw new Error([
`Deploy is only supported in test mode, test mode was set to 'false'. Test mode is only possible when the configured network is 'lightnet' and the configured network is '${__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_network, "f")}'.`,
`Please see the README.md within the 'contracts/mina' workspace of the 'nori-bridge-sdk' repository and use the deploy script 'npm run deploy <storeHash>' instead of this method.`,
].join('\n'));
}
logger.log('Creating deploy transaction.');
const noriHeliosProgramPi0 = FrC.from(bridgeHeadNoriSP1HeliosProgramPi0);
const proofConversionPO2 = Field.from(proofConversionSP1ToPlonkPO2);
const senderPublicKey = __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_senderPrivateKey, "f").toPublicKey();
const initialStoreHash = Bytes32FieldPair.fromBytes32(storeHash);
const tokenBasePrivateKey = PrivateKey.random();
const tokenBaseAddress = tokenBasePrivateKey.toPublicKey();
const tokenBase = new FungibleToken(tokenBaseAddress);
const deployTx = await Mina.transaction({ sender: senderPublicKey, fee: __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_txFee, "f") }, async () => {
AccountUpdate.fundNewAccount(senderPublicKey, 3);
logger.log(`Deploying NoriTokenBridge with verification key hash: '${__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_instances, "a", _NoriTokenBridgeSubmitter_noriTokenBridgeVerificationKey_get).hash}'`);
await __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_zkApp, "f").deploy({
verificationKey: __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_instances, "a", _NoriTokenBridgeSubmitter_noriTokenBridgeVerificationKey_get),
adminPublicKey: senderPublicKey,
tokenBaseAddress,
storageVKHash: this.noriStorageInterfaceVerificationKey.hash,
newStoreHash: initialStoreHash,
ethTokenBridgeAddress,
noriHeliosProgramPi0,
proofConversionPO2,
genesisRoot,
});
await tokenBase.deploy({
symbol: 'nETH',
src: 'https://github.com/2nori/nori-bridge-sdk',
allowUpdates: true,
});
await tokenBase.initialize(__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_possibleTokenBridgePrivateKey, "f").toPublicKey(), UInt8.from(6), Bool(false));
});
logger.log('Deploy transaction created successfully. Proving...');
await deployTx.prove();
logger.log('Transaction proved. Signing and sending the transaction...');
await deployTx
.sign([
__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_senderPrivateKey, "f"),
__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_possibleTokenBridgePrivateKey, "f"),
tokenBasePrivateKey,
])
.send()
.wait();
logger.log('NoriTokenBridge and FungibleToken deployed successfully.');
}
async createProof(proofArguments) {
try {
logger.log('Creating proof.');
const { sp1PlonkProof, conversionOutputProof } = proofArguments;
const rawProof = await NodeProofLeft.fromJSON(conversionOutputProof.proofData);
logger.log('Decoding converted proof and creating verification inputs.');
const ethInput = new EthInput(decodeConsensusMptProof(sp1PlonkProof));
logger.log('Proof arguments decoded successfully.');
return { ethInput, rawProof };
}
catch (err) {
logger.error(`Error creating proof: ${String(err)}`);
throw err;
}
}
async submit({ ethInput, rawProof }) {
logger.log('Submitting a proof.');
try {
await fetchAccount({ publicKey: __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_zkApp, "f").address });
await fetchAccount({
publicKey: __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_senderPrivateKey, "f").toPublicKey(),
});
logger.log('Fetched accounts.');
logger.log('Creating update transaction.');
const updateTx = await Mina.transaction({
sender: __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_senderPrivateKey, "f").toPublicKey(),
fee: __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_txFee, "f"),
memo: `State for slot ${ethInput.outputSlot.toString()} set`,
}, async () => {
await __classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_zkApp, "f").update(ethInput, rawProof);
});
await updateTx.prove();
logger.log('Transaction proven.');
const tx = await updateTx.sign([__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_senderPrivateKey, "f")]).send();
logger.log(`Transaction sent to '${__classPrivateFieldGet(this, _NoriTokenBridgeSubmitter_network, "f")}'.`);
if (!tx.data) {
throw new Error('Transaction data is undefined');
}
const txId = tx.data.sendZkapp.zkapp.id;
const txHash = tx.data.sendZkapp.zkapp.hash;
if (!txId) {
throw new Error('txId is undefined');
}
return {
txId,
txHash,
};
}
catch (err) {
logger.error(`Error submitting proof: ${String(err)}`);
throw err;
}
}
}
_NoriTokenBridgeSubmitter_zkApp = new WeakMap(), _NoriTokenBridgeSubmitter_senderPrivateKey = new WeakMap(), _NoriTokenBridgeSubmitter_possibleTokenBridgePrivateKey = new WeakMap(), _NoriTokenBridgeSubmitter_network = new WeakMap(), _NoriTokenBridgeSubmitter_txFee = new WeakMap(), _NoriTokenBridgeSubmitter_testMode = new WeakMap(), _NoriTokenBridgeSubmitter_instances = new WeakSet(), _NoriTokenBridgeSubmitter_noriTokenBridgeVerificationKey_get = function _NoriTokenBridgeSubmitter_noriTokenBridgeVerificationKey_get() {
return this.noriTokenBridgeVerificationKey;
};
//# sourceMappingURL=proofSubmitter.js.map