@lumina-dex/contracts
Version:
Contracts for Lumina DEX.
506 lines (457 loc) • 20.6 kB
text/typescript
import {
AccountUpdate,
AccountUpdateForest,
Bool,
DeployArgs,
Field,
MerkleMap,
MerkleMapWitness,
method,
Permissions,
Poseidon,
PublicKey,
Signature,
State,
state,
Struct,
TokenContract,
TokenId,
UInt32,
UInt64,
VerificationKey
} from "o1js"
import { FungibleToken } from "../indexpool.js"
import {
Multisig,
MultisigInfo,
MultisigSigner,
SignatureInfo,
SignatureRight,
UpdateAccountInfo,
UpdateFactoryInfo,
UpdateSignerData,
verifySignature
} from "./Multisig.js"
/**
* Current verification key of pool contract
*/
export const contractData =
"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"
/**
* Current hash of verification key of pool contract
*/
export const contractHash = Field(5594455694225844493828763509468165382923180297395536400245484306269052311024n)
/**
* Current verification key of pool token holder contract
*/
export const contractHolderData =
"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"
/**
* Current hash of verification key of pool token holder contract
*/
export const contractHolderHash = Field(11549450361008775235242038734709709351447737775285423036140322613088171224061n)
/**
* Interface of current data needed to deploy the pool factory
*/
export interface PoolDeployProps extends Exclude<DeployArgs, undefined> {
symbol: string
src: string
protocol: PublicKey
delegator: PublicKey
approvedSigner: Field
signatures: SignatureInfo[]
multisigInfo: MultisigInfo
}
/**
* Event emitted when a new pool is created
*/
export class PoolCreationEvent extends Struct({
sender: PublicKey,
signer: PublicKey,
poolAddress: PublicKey,
token0Address: PublicKey,
token1Address: PublicKey
}) {
constructor(value: {
sender: PublicKey
signer: PublicKey
poolAddress: PublicKey
token0Address: PublicKey
token1Address: PublicKey
}) {
super(value)
}
}
/**
* Event emitted when an address is updated
*/
export class UpdateUserEvent extends Struct({
newUser: PublicKey
}) {
constructor(
newUser: PublicKey
) {
super({ newUser })
}
}
/**
* Event emitted when the verification key is updated
*/
export class UpdateVerificationKeyEvent extends Struct({
hash: Field
}) {
constructor(
hash: Field
) {
super({ hash })
}
}
/**
* Event emitted when the signer list is updated
*/
export class UpdateSignerEvent extends Struct({
root: Field
}) {
constructor(
root: Field
) {
super({ root })
}
}
/**
* Factory who create pools
*/
export class PoolFactory extends TokenContract {
/**
* List of signer approved to deploy a new pool
*/
approvedSigner = State<Field>()
/**
* Account who collect protocol fees
*/
protocol = State<PublicKey>()
/**
* Delegator account for mina pools
*/
delegator = State<PublicKey>()
/**
* List of pool factory events
*/
events = {
poolAdded: PoolCreationEvent,
upgrade: UpdateVerificationKeyEvent,
updateSigner: UpdateSignerEvent,
updateProtocol: UpdateUserEvent,
updateDelegator: UpdateUserEvent,
updateOwner: UpdateUserEvent
}
/**
* Method call when you deploy the pool factory contracts
* @param args default data stored in the contracts
*/
async deploy(args: PoolDeployProps) {
await super.deploy(args)
const defaultRoot = new MerkleMap().getRoot()
args.approvedSigner.equals(Field.empty()).assertFalse("Approved signer is empty")
args.approvedSigner.equals(defaultRoot).assertFalse("Approved signer is empty")
this.network.globalSlotSinceGenesis.requireBetween(UInt32.zero, args.multisigInfo.deadlineSlot)
const updateSignerData = new UpdateSignerData({
oldRoot: Field.empty(),
newRoot: args.approvedSigner,
deadlineSlot: args.multisigInfo.deadlineSlot
})
// we need 3 signatures to update signer, prevent to deadlock contract update
const right = SignatureRight.canUpdateSigner()
verifySignature(
args.signatures,
args.multisigInfo.deadlineSlot,
args.multisigInfo,
args.multisigInfo.approvedUpgrader,
updateSignerData.toFields(),
right
)
this.account.zkappUri.set(args.src)
this.account.tokenSymbol.set(args.symbol)
this.approvedSigner.set(args.approvedSigner)
this.protocol.set(args.protocol)
this.delegator.set(args.delegator)
const permissions = Permissions.default()
permissions.access = Permissions.proof()
permissions.setPermissions = Permissions.impossible()
permissions.setVerificationKey = Permissions.VerificationKey.proofDuringCurrentVersion()
this.account.permissions.set(permissions)
}
/**
* Upgrade to a new version
* @param multisig multisig data
* @param vk new verification key
*/
async updateVerificationKey(multisig: Multisig, vk: VerificationKey) {
const deadlineSlot = multisig.info.deadlineSlot
const approvedSigner = this.approvedSigner.getAndRequireEquals()
multisig.info.approvedUpgrader.equals(approvedSigner).assertTrue("Incorrect signer list")
this.network.globalSlotSinceGenesis.requireBetween(UInt32.zero, deadlineSlot)
const upgradeInfo = new UpdateFactoryInfo({ newVkHash: vk.hash, deadlineSlot })
multisig.verifyUpdateFactory(upgradeInfo)
this.account.verificationKey.set(vk)
this.emitEvent("upgrade", new UpdateVerificationKeyEvent(vk.hash))
}
/**
* Update the list of approved signers
* @param multisig multisig data
* @param newRoot merkle root of the new list
*/
async updateApprovedSigner(multisig: MultisigSigner, newRoot: Field) {
const oldRoot = this.approvedSigner.getAndRequireEquals()
multisig.info.approvedUpgrader.equals(oldRoot).assertTrue("Incorrect signer list")
const deadlineSlot = multisig.info.deadlineSlot
this.network.globalSlotSinceGenesis.requireBetween(UInt32.zero, deadlineSlot)
const upgradeInfo = new UpdateSignerData({ oldRoot, newRoot, deadlineSlot })
multisig.verifyUpdateSigner(upgradeInfo)
this.approvedSigner.set(newRoot)
this.emitEvent("updateSigner", new UpdateSignerEvent(newRoot))
}
/**
* Update the protocol account address
* @param multisig multisig data
* @param newUser address of the new protocol collectord
*/
async setNewProtocol(multisig: Multisig, newUser: PublicKey) {
const oldUser = this.protocol.getAndRequireEquals()
const deadlineSlot = multisig.info.deadlineSlot
const approvedSigner = this.approvedSigner.getAndRequireEquals()
multisig.info.approvedUpgrader.equals(approvedSigner).assertTrue("Incorrect signer list")
this.network.globalSlotSinceGenesis.requireBetween(UInt32.zero, deadlineSlot)
const upgradeInfo = new UpdateAccountInfo({ oldUser, newUser, deadlineSlot })
multisig.verifyUpdateProtocol(upgradeInfo)
this.protocol.set(newUser)
this.emitEvent("updateProtocol", new UpdateUserEvent(newUser))
}
/**
* Update the delgator address
* @param multisig multisig data
* @param newUser address of the new delegator
*/
async setNewDelegator(multisig: Multisig, newUser: PublicKey) {
const oldUser = this.delegator.getAndRequireEquals()
const deadlineSlot = multisig.info.deadlineSlot
const approvedSigner = this.approvedSigner.getAndRequireEquals()
multisig.info.approvedUpgrader.equals(approvedSigner).assertTrue("Incorrect signer list")
this.network.globalSlotSinceGenesis.requireBetween(UInt32.zero, deadlineSlot)
const upgradeInfo = new UpdateAccountInfo({ oldUser, newUser, deadlineSlot })
multisig.verifyUpdateDelegator(upgradeInfo)
this.delegator.set(newUser)
this.emitEvent("updateDelegator", new UpdateUserEvent(newUser))
}
/**
* Get protocol address
* @returns address of the protocol
*/
.returns(PublicKey)
async getProtocol() {
const protocol = this.protocol.getAndRequireEquals()
return protocol
}
/**
* Get delegator address
* @returns address of the delegator
*/
.returns(PublicKey)
async getDelegator() {
const delegator = this.delegator.getAndRequireEquals()
return delegator
}
/**
* Get approved signer
* @returns root of approved signer
*/
.returns(Field)
async getApprovedSigner() {
const approvedSigner = this.approvedSigner.getAndRequireEquals()
return approvedSigner
}
/**
* Method use by token allowance but it's not permissible to use it
* @param forest account forest to update
*/
async approveBase(forest: AccountUpdateForest) {
forest.isEmpty().assertTrue("You can't approve any token operation")
}
/**
* Create a new mina/token pool
* @param newAccount address of the new pool
* @param token token 1 for the mina pool
* @param signer who sign the argument
* @param signature who proves you can deploy this pool (only approved signer can deploy a pool)
* @param path merkle witness to check if signer is in the approved list
* @param right right of the signer
*/
async createPool(
newAccount: PublicKey,
token: PublicKey,
signer: PublicKey,
signature: Signature,
path: MerkleMapWitness,
right: SignatureRight
) {
token.isEmpty().assertFalse("Token is empty")
await this.createAccounts(newAccount, token, PublicKey.empty(), token, signer, signature, path, right, false)
}
/**
* Create a new token/token pool
* @param newAccount address of the new pool
* @param token 0 of the pool
* @param token 1 of the pool
* @param signer who sign the argument
* @param signature who proves you can deploy this pool (only approved signer can deploy a pool)
* @param path merkle witness to check if signer is in the approved list
* @param right right of the signer
*/
async createPoolToken(
newAccount: PublicKey,
token0: PublicKey,
token1: PublicKey,
signer: PublicKey,
signature: Signature,
path: MerkleMapWitness,
right: SignatureRight
) {
token0.x.assertLessThan(token1.x, "Token 0 need to be lesser than token 1")
// create an address with the 2 public key as pool id
const fields = token0.toFields().concat(token1.toFields())
const hash = Poseidon.hashToGroup(fields)
const publicKey = PublicKey.fromGroup(hash)
publicKey.isEmpty().assertFalse("publicKey is empty")
await this.createAccounts(newAccount, publicKey, token0, token1, signer, signature, path, right, true)
}
private async createAccounts(
newAccount: PublicKey,
token: PublicKey,
token0: PublicKey,
token1: PublicKey,
signer: PublicKey,
signature: Signature,
path: MerkleMapWitness,
right: SignatureRight,
isTokenPool: boolean
) {
const tokenAccount = AccountUpdate.create(token, this.deriveTokenId())
// if the balance is not zero, so a pool already exist for this token
tokenAccount.account.balance.requireEquals(UInt64.zero)
// verify the signer has right to create the pool
signer.equals(PublicKey.empty()).assertFalse("Empty signer")
const signerHash = Poseidon.hash(signer.toFields())
const approvedSignerRoot = this.approvedSigner.getAndRequireEquals()
right.deployPool.assertTrue("Insufficient right to deploy a pool")
const [root, key] = path.computeRootAndKey(right.hash())
root.assertEquals(approvedSignerRoot, "Invalid signer merkle root")
key.assertEquals(signerHash, "Invalid signer")
signature.verify(signer, newAccount.toFields()).assertTrue("Invalid signature")
// create a pool as this new address
const poolAccount = AccountUpdate.createSigned(newAccount)
// Require this account didn't already exist
poolAccount.account.isNew.requireEquals(Bool(true))
// set pool account vk and permission
poolAccount.body.update.verificationKey = { isSome: Bool(true), value: { data: contractData, hash: contractHash } }
poolAccount.body.update.permissions = {
isSome: Bool(true),
value: {
...Permissions.default(),
// only proof to prevent signature owner to steal liquidity
access: Permissions.proof(),
setVerificationKey: Permissions.VerificationKey.proofDuringCurrentVersion(),
send: Permissions.proof(),
setDelegate: Permissions.proof(),
setPermissions: Permissions.impossible()
}
}
// set poolAccount initial state
const appState = this.createState(token0, token1)
poolAccount.body.update.appState = appState
// Liquidity token default name
poolAccount.account.tokenSymbol.set("LUM")
// create a token holder as this new address
if (isTokenPool) {
// only pool token need an account at token 0 address
await this.createPoolHolderAccount(newAccount, token0, appState)
}
await this.createPoolHolderAccount(newAccount, token1, appState)
// create a liquidity token holder as this new address
const tokenId = TokenId.derive(newAccount)
const liquidityAccount = AccountUpdate.createSigned(newAccount, tokenId)
// Require this account didn't already exist
liquidityAccount.account.isNew.requireEquals(Bool(true))
liquidityAccount.body.update.permissions = {
isSome: Bool(true),
value: {
...Permissions.default(),
setVerificationKey: Permissions.VerificationKey.impossibleDuringCurrentVersion(),
// This is necessary in order to allow burn circulation supply without signature
send: Permissions.none(),
setPermissions: Permissions.impossible()
}
}
// we mint one token to check if this pool exist
this.internal.mint({ address: tokenAccount, amount: UInt64.one })
await poolAccount.approve(liquidityAccount)
const sender = this.sender.getAndRequireSignature()
this.emitEvent(
"poolAdded",
new PoolCreationEvent({ sender, signer, poolAddress: newAccount, token0Address: token0, token1Address: token1 })
)
}
private createState(token0: PublicKey, token1: PublicKey): { isSome: Bool; value: Field }[] {
const token0Fields = token0.toFields()
const token1Fields = token1.toFields()
const poolFactory = this.address.toFields()
const protocol = this.protocol.getAndRequireEquals()
const protocolFields = protocol.toFields()
return [
{ isSome: Bool(true), value: token0Fields[0] },
{ isSome: Bool(true), value: token0Fields[1] },
{ isSome: Bool(true), value: token1Fields[0] },
{ isSome: Bool(true), value: token1Fields[1] },
{ isSome: Bool(true), value: poolFactory[0] },
{ isSome: Bool(true), value: poolFactory[1] },
{ isSome: Bool(true), value: protocolFields[0] },
{ isSome: Bool(true), value: protocolFields[1] }
]
}
private async createPoolHolderAccount(
newAccount: PublicKey,
token: PublicKey,
appState: { isSome: Bool; value: Field }[]
): Promise<AccountUpdate> {
const fungibleToken = new FungibleToken(token)
const poolHolderAccount = AccountUpdate.createSigned(newAccount, fungibleToken.deriveTokenId())
// Require this account didn't already exist
poolHolderAccount.account.isNew.requireEquals(Bool(true))
// set pool token holder account vk and permission
poolHolderAccount.body.update.verificationKey = {
isSome: Bool(true),
value: { data: contractHolderData, hash: contractHolderHash }
}
poolHolderAccount.body.update.permissions = {
isSome: Bool(true),
value: {
...Permissions.default(),
setVerificationKey: Permissions.VerificationKey.proofDuringCurrentVersion(),
send: Permissions.proof(),
setPermissions: Permissions.impossible()
}
}
poolHolderAccount.body.update.appState = appState
await fungibleToken.approveAccountUpdate(poolHolderAccount)
return poolHolderAccount
}
}