@nori-zk/mina-token-bridge
Version:
A Mina zk-program contract allowing users to mint tokens on Nori Bridge.
331 lines • 21.1 kB
JavaScript
var _NoriTokenControllerSubmitter_instances, _NoriTokenControllerSubmitter_senderPrivateKey, _NoriTokenControllerSubmitter_network, _NoriTokenControllerSubmitter_txFee, _NoriTokenControllerSubmitter_noriTokenController, _NoriTokenControllerSubmitter_tokenBase, _NoriTokenControllerSubmitter_noriTokenControllerPrivateKey, _NoriTokenControllerSubmitter_tokenBasePrivateKey, _NoriTokenControllerSubmitter_adminPublicKey, _NoriTokenControllerSubmitter_ethProcessorAddress, _NoriTokenControllerSubmitter_mock, _NoriTokenControllerSubmitter_cache, _NoriTokenControllerSubmitter_compilePrerequisites, _NoriTokenControllerSubmitter_isMintProofData;
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
import 'dotenv/config';
import { AccountUpdate, Mina, PrivateKey, PublicKey, fetchAccount, UInt8, Bool, Cache, } from 'o1js';
import { FungibleToken } from './TokenBase.js';
import { NoriStorageInterface } from './NoriStorageInterface.js';
/*import {
MockNoriTokenController,
MockConsenusProof,
MockDepositAttesterProof,
MockMinaAttestationProof,
MockMintProofData,
} from './NoriTokenControllerMock.js';*/
import { ContractDepositAttestor } from '@nori-zk/o1js-zk-utils';
import { EthVerifier } from '@nori-zk/o1js-zk-utils';
import { EthDepositProgram } from './e2ePrerequisites.js';
import { NoriTokenController } from './NoriTokenController.js';
import { compileEcdsaEthereum, compileEcdsaSigPresentationVerifier, } from './credentialAttestation.js';
export class NoriTokenControllerSubmitter {
constructor(config) {
_NoriTokenControllerSubmitter_instances.add(this);
_NoriTokenControllerSubmitter_senderPrivateKey.set(this, void 0);
_NoriTokenControllerSubmitter_network.set(this, void 0);
_NoriTokenControllerSubmitter_txFee.set(this, void 0);
// Contract instances
_NoriTokenControllerSubmitter_noriTokenController.set(this, void 0); // MockNoriTokenController
_NoriTokenControllerSubmitter_tokenBase.set(this, void 0);
// Optional private keys for deployment
_NoriTokenControllerSubmitter_noriTokenControllerPrivateKey.set(this, void 0);
_NoriTokenControllerSubmitter_tokenBasePrivateKey.set(this, void 0);
_NoriTokenControllerSubmitter_adminPublicKey.set(this, void 0);
_NoriTokenControllerSubmitter_ethProcessorAddress.set(this, void 0);
_NoriTokenControllerSubmitter_mock.set(this, void 0);
_NoriTokenControllerSubmitter_cache.set(this, Cache.FileSystem('./cache'));
console.log(`🛠 NoriTokenControllerSubmitter constructor called!`);
const errors = [];
// Validate required config
if (!config.senderPrivateKey) {
errors.push('senderPrivateKey is required');
}
if (!config.network) {
errors.push('network is required');
}
if (!config.networkUrl) {
errors.push('networkUrl is required');
}
// For deployment, we need additional keys
const isDeployment = !config.noriTokenControllerAddress || !config.tokenBaseAddress;
if (isDeployment) {
if (!config.noriTokenControllerPrivateKey) {
errors.push('noriTokenControllerPrivateKey is required for deployment');
}
if (!config.tokenBasePrivateKey) {
errors.push('tokenBasePrivateKey is required for deployment');
}
if (!config.adminPublicKey) {
errors.push('adminPublicKey is required for deployment');
}
// if (!config.ethProcessorAddress) { //TODO enable when needed
// errors.push('ethProcessorAddress is required for deployment');
// }
}
if (errors.length > 0) {
throw new Error(`Configuration errors:\n- ${errors.join('\n- ')}`);
}
// Set instance variables
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_senderPrivateKey, PrivateKey.fromBase58(config.senderPrivateKey), "f");
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_network, config.network, "f");
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_txFee, (config.txFee || 0.1) * 1e9, "f");
this.minaRPCNetworkUrl = config.networkUrl;
// Set deployment keys if provided
if (config.noriTokenControllerPrivateKey) {
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_noriTokenControllerPrivateKey, PrivateKey.fromBase58(config.noriTokenControllerPrivateKey), "f");
}
if (config.tokenBasePrivateKey) {
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_tokenBasePrivateKey, PrivateKey.fromBase58(config.tokenBasePrivateKey), "f");
}
if (config.adminPublicKey) {
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_adminPublicKey, PublicKey.fromBase58(config.adminPublicKey), "f");
}
if (config.ethProcessorAddress) {
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_ethProcessorAddress, PublicKey.fromBase58(config.ethProcessorAddress), "f");
}
// Initialize contract instances
const noriAddress = config.noriTokenControllerAddress
? PublicKey.fromBase58(config.noriTokenControllerAddress)
: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenControllerPrivateKey, "f").toPublicKey();
const tokenAddress = config.tokenBaseAddress
? PublicKey.fromBase58(config.tokenBaseAddress)
: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBasePrivateKey, "f").toPublicKey();
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_mock, !!config.mock || false, "f");
console.log('this.#mock', __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f"));
console.log('this.#mock', __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f"), __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f") ? 'selecting mock' : 'selecting real');
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_noriTokenController, /*this.#mock
? new MockNoriTokenController(noriAddress)
:*/ new NoriTokenController(noriAddress), "f");
__classPrivateFieldSet(this, _NoriTokenControllerSubmitter_tokenBase, new FungibleToken(tokenAddress), "f");
console.log('NoriTokenControllerSubmitter initialized successfully');
}
async networkSetUp() {
console.log(`Setting up ${__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_network, "f")} network with RPC endpoint: '${this.minaRPCNetworkUrl}'.`);
const Network = Mina.Network({
networkId: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_network, "f"),
mina: this.minaRPCNetworkUrl,
// ...(this.#network.toString().match('localhost') && {
// lightnetAccountManager: 'http://localhost:8181',
// }),
});
Mina.setActiveInstance(Network);
console.log('Finished Mina network setup.');
}
async compileContracts() {
console.log('Compiling prerequisites...');
await __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_instances, "m", _NoriTokenControllerSubmitter_compilePrerequisites).call(this);
console.log('Compiling contracts...');
// Compile all required contracts
console.log('Compiling NoriStorageInterface...');
const storageResult = await NoriStorageInterface.compile({
cache: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_cache, "f"),
});
console.log('Compiling FungibleToken...');
const tokenBaseResult = await FungibleToken.compile({
cache: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_cache, "f"),
});
console.log('Compiling NoriTokenController...');
console.log('this.#mock', __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f"), __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f") ? 'compiling mock' : 'compiling real');
const controllerResult = /*this.#mock
? await MockNoriTokenController.compile({
cache: this.#cache,
})
: */ await NoriTokenController.compile({
cache: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_cache, "f"),
});
//TODO replace with compileAndVerifyContracts
this.storageInterfaceVerificationKey = storageResult.verificationKey;
this.tokenBaseVerificationKey = tokenBaseResult.verificationKey;
this.noriTokenControllerVerificationKey =
controllerResult.verificationKey;
console.log('All contracts compiled successfully');
}
async deployContracts(options = {}) {
if (!__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenControllerPrivateKey, "f") ||
!__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBasePrivateKey, "f") ||
!__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_adminPublicKey, "f")) {
throw new Error('Deployment keys not provided in constructor');
}
console.log('Deploying NoriTokenController and TokenBase contracts...');
const symbol = options.symbol || 'nETH';
const decimals = UInt8.from(options.decimals || 18);
const allowUpdates = options.allowUpdates ?? true;
const startPaused = Bool(options.startPaused ?? false);
const senderPublicKey = __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_senderPrivateKey, "f").toPublicKey();
console.log('this.#mock', __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f"), __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f") ? 'deploying mock' : 'deploying real');
const deployTx = await Mina.transaction({ sender: senderPublicKey, fee: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_txFee, "f") }, async () => {
AccountUpdate.fundNewAccount(senderPublicKey, 3);
// Deploy NoriTokenController
await __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").deploy({
adminPublicKey: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_adminPublicKey, "f"),
tokenBaseAddress: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").address,
storageVKHash: this.storageInterfaceVerificationKey.hash,
ethProcessorAddress: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_ethProcessorAddress, "f") ||
PrivateKey.random().toPublicKey(),
});
// Deploy TokenBase
await __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").deploy({
symbol,
src: 'https://x.com/nori_zk',
allowUpdates,
});
// Initialize TokenBase
await __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").initialize(__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").address, decimals, startPaused);
});
console.log('Deploy transaction created. Proving...');
await deployTx.prove();
console.log('Transaction proved. Signing and sending...');
const tx = await deployTx
.sign([
__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_senderPrivateKey, "f"),
__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenControllerPrivateKey, "f"),
__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBasePrivateKey, "f"),
])
.send();
const result = await tx.wait();
console.log('Contracts deployed successfully');
return {
noriTokenControllerAddress: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").address.toBase58(),
tokenBaseAddress: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").address.toBase58(),
txHash: result.hash,
};
}
async setupStorage(userPublicKey) {
console.log(`Setting up storage for user: ${userPublicKey.toBase58()}`);
await this.fetchAccounts([
userPublicKey,
__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").address,
]);
console.log('this.#mock', __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f"), __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f") ? 'setup mock' : 'setup real');
const setupTx = await Mina.transaction({ sender: userPublicKey, fee: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_txFee, "f") }, async () => {
AccountUpdate.fundNewAccount(userPublicKey, 1);
await __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").setUpStorage(userPublicKey, this.storageInterfaceVerificationKey);
});
await setupTx.prove();
const tx = await setupTx.sign([__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_senderPrivateKey, "f")]).send();
const result = await tx.wait();
console.log('Storage setup completed successfully');
return { txHash: result.hash };
}
//TODO make one that returns unsigned transaction to be passed to the wallet
async mint(userPublicKey, proofData, // MockMintProofData |
userPrivateKey, fundNewAccount = true) {
console.log(`Minting tokens for user: ${userPublicKey.toBase58()}`);
await this.fetchAccounts([
userPublicKey,
__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").address,
]);
console.log('this.#mock', __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f"), __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f") ? 'mint mock' : 'mint real');
const mintTx = await Mina.transaction({ sender: userPublicKey, fee: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_txFee, "f") }, async () => {
if (fundNewAccount) {
AccountUpdate.fundNewAccount(userPublicKey, 1);
}
/*if (this.#mock) {
const mockNoriTokenController = this
.#noriTokenController as MockNoriTokenController;
const mockProofData = proofData as MockMintProofData;
await mockNoriTokenController.noriMint(
mockProofData.ethConsensusProof,
mockProofData.depositAttesterProof,
mockProofData.minaAttestationProof
);
} else {*/
const noriTokenController = __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f");
const realProofData = proofData;
await noriTokenController.noriMint(realProofData.ethDepositProof, realProofData.presentationProof);
//}
});
await mintTx.prove();
const tx = await mintTx
.sign([__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_senderPrivateKey, "f"), userPrivateKey])
.send();
const result = await tx.wait();
// Fetch updated balance
await fetchAccount({
publicKey: userPublicKey,
tokenId: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").deriveTokenId(),
});
const balance = await __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").getBalanceOf(userPublicKey);
console.log('Minting completed successfully');
//if (this.#isMintProofData(proofData)) {
return {
txHash: result.hash,
mintedAmount: proofData.ethDepositProof.publicOutput.totalLocked.toString(),
userBalance: balance.toString(),
};
/*} else {
return {
txHash: result.hash,
mintedAmount:
proofData.depositAttesterProof?.lockedSoFar.toString(),
userBalance: balance.toString(),
};
}*/
}
async getUserBalance(userPublicKey) {
await fetchAccount({
publicKey: userPublicKey,
tokenId: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").deriveTokenId(),
});
return __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").getBalanceOf(userPublicKey);
}
async getUserStorageInfo(userPublicKey) {
const storage = new NoriStorageInterface(userPublicKey, __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").deriveTokenId());
await fetchAccount({
publicKey: userPublicKey,
tokenId: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").deriveTokenId(),
});
const userKeyHash = await storage.userKeyHash.fetch();
const mintedSoFar = await storage.mintedSoFar.fetch();
return { userKeyHash, mintedSoFar };
}
// Getters for contract addresses
get noriTokenControllerAddress() {
return __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f").address.toBase58();
}
get tokenBaseAddress() {
return __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f").address.toBase58();
}
get contracts() {
return {
noriTokenController: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_noriTokenController, "f"),
tokenBase: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_tokenBase, "f"),
};
}
async fetchAccounts(accounts) {
await Promise.all(accounts.map((addr) => fetchAccount({ publicKey: addr })));
}
}
_NoriTokenControllerSubmitter_senderPrivateKey = new WeakMap(), _NoriTokenControllerSubmitter_network = new WeakMap(), _NoriTokenControllerSubmitter_txFee = new WeakMap(), _NoriTokenControllerSubmitter_noriTokenController = new WeakMap(), _NoriTokenControllerSubmitter_tokenBase = new WeakMap(), _NoriTokenControllerSubmitter_noriTokenControllerPrivateKey = new WeakMap(), _NoriTokenControllerSubmitter_tokenBasePrivateKey = new WeakMap(), _NoriTokenControllerSubmitter_adminPublicKey = new WeakMap(), _NoriTokenControllerSubmitter_ethProcessorAddress = new WeakMap(), _NoriTokenControllerSubmitter_mock = new WeakMap(), _NoriTokenControllerSubmitter_cache = new WeakMap(), _NoriTokenControllerSubmitter_instances = new WeakSet(), _NoriTokenControllerSubmitter_compilePrerequisites = async function _NoriTokenControllerSubmitter_compilePrerequisites() {
if (__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_mock, "f"))
return;
// Compile programs / contracts
console.time('compileEcdsaEthereum');
await compileEcdsaEthereum(__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_cache, "f"));
console.timeEnd('compileEcdsaEthereum'); // 1:20.330 (m:ss.mmm)
console.time('compilePresentationVerifier');
await compileEcdsaSigPresentationVerifier(__classPrivateFieldGet(this, _NoriTokenControllerSubmitter_cache, "f"));
console.timeEnd('compilePresentationVerifier'); // 11.507s
console.time('ContractDepositAttestor compile');
const { verificationKey: contractDepositAttestorVerificationKey } = await ContractDepositAttestor.compile({
cache: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_cache, "f"),
forceRecompile: true,
});
console.timeEnd('ContractDepositAttestor compile');
console.log(`ContractDepositAttestor contract compiled vk: '${contractDepositAttestorVerificationKey.hash}'.`);
console.time('EthVerifier compile');
const { verificationKey: ethVerifierVerificationKey } = await EthVerifier.compile({
cache: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_cache, "f"),
forceRecompile: true,
});
console.timeEnd('EthVerifier compile');
console.log(`EthVerifier compiled vk: '${ethVerifierVerificationKey.hash}'.`);
console.time('EthDepositProgram compile');
const { verificationKey: EthDepositProgramVerificationKey } = await EthDepositProgram.compile({
cache: __classPrivateFieldGet(this, _NoriTokenControllerSubmitter_cache, "f"),
forceRecompile: true,
});
console.timeEnd('EthDepositProgram compile');
console.log(`EthDepositProgram compiled vk: '${EthDepositProgramVerificationKey.hash}'.`);
}, _NoriTokenControllerSubmitter_isMintProofData = function _NoriTokenControllerSubmitter_isMintProofData(obj) {
return 'ethDepositProof' in obj && 'presentationProof' in obj;
};
//# sourceMappingURL=NoriControllerSubmitter.js.map