diff --git a/contracts/flow/teleport/TeleportCustodyAptos.cdc b/contracts/flow/teleport/TeleportCustodyAptos.cdc new file mode 100644 index 0000000..4b2fd20 --- /dev/null +++ b/contracts/flow/teleport/TeleportCustodyAptos.cdc @@ -0,0 +1,180 @@ +import FungibleToken from "../token/FungibleToken.cdc" +import BloctoToken from "../token/BloctoToken.cdc" + +pub contract TeleportCustodyAptos { + + pub event TeleportAdminCreated(allowedAmount: UFix64) + pub event Locked(amount: UFix64, to: [UInt8]) + pub event Unlocked(amount: UFix64, from: [UInt8], txHash: String) + pub event FeeCollected(amount: UFix64, type: UInt8) + + pub let AdminStoragePath: StoragePath + pub let TeleportAdminStoragePath: StoragePath + pub let TeleportAdminTeleportUserPath: PublicPath + pub let TeleportAdminTeleportControlPath: PrivatePath + pub let teleportAddressLength: Int + pub let teleportTxHashLength: Int + + pub var isFrozen: Bool + access(contract) var unlocked: {String: Bool} + access(contract) let lockVault: @BloctoToken.Vault + + pub resource Allowance { + pub var balance: UFix64 + + init(balance: UFix64) { + self.balance = balance + } + } + + pub resource Administrator { + + pub fun createNewTeleportAdmin(allowedAmount: UFix64): @TeleportAdmin { + emit TeleportAdminCreated(allowedAmount: allowedAmount) + return <- create TeleportAdmin(allowedAmount: allowedAmount) + } + + pub fun freeze() { + TeleportCustodyAptos.isFrozen = true + } + + pub fun unfreeze() { + TeleportCustodyAptos.isFrozen = false + } + + pub fun createAllowance(allowedAmount: UFix64): @Allowance { + return <- create Allowance(balance: allowedAmount) + } + } + + pub resource interface TeleportUser { + pub var lockFee: UFix64 + + pub var unlockFee: UFix64 + + pub var allowedAmount: UFix64 + + pub fun lock(from: @FungibleToken.Vault, to: [UInt8]) + + pub fun depositAllowance(from: @Allowance) + } + + pub resource interface TeleportControl { + pub fun unlock(amount: UFix64, from: [UInt8], txHash: String): @FungibleToken.Vault + + pub fun withdrawFee(amount: UFix64): @FungibleToken.Vault + + pub fun updateLockFee(fee: UFix64) + + pub fun updateUnlockFee(fee: UFix64) + } + + pub resource TeleportAdmin: TeleportUser, TeleportControl { + pub var lockFee: UFix64 + + pub var unlockFee: UFix64 + + pub var allowedAmount: UFix64 + + pub let feeCollector: @BloctoToken.Vault + + pub fun lock(from: @FungibleToken.Vault, to: [UInt8]) { + pre { + !TeleportCustodyAptos.isFrozen: "Teleport service is frozen" + to.length == TeleportCustodyAptos.teleportAddressLength: "Teleport address should be teleportAddressLength bytes" + } + + let vault <- from as! @BloctoToken.Vault + let fee <- vault.withdraw(amount: self.lockFee) + + self.feeCollector.deposit(from: <-fee) + + let amount = vault.balance + TeleportCustodyAptos.lockVault.deposit(from: <-vault) + + emit Locked(amount: amount, to: to) + emit FeeCollected(amount: self.lockFee, type: 0) + } + + pub fun unlock(amount: UFix64, from: [UInt8], txHash: String): @FungibleToken.Vault { + pre { + !TeleportCustodyAptos.isFrozen: "Teleport service is frozen" + amount <= self.allowedAmount: "Amount unlocked must be less than the allowed amount" + amount > self.unlockFee: "Amount unlocked must be greater than unlock fee" + from.length == TeleportCustodyAptos.teleportAddressLength: "Teleport address should be teleportAddressLength bytes" + txHash.length == TeleportCustodyAptos.teleportTxHashLength: "Teleport tx hash should be teleportTxHashLength bytes" + !(TeleportCustodyAptos.unlocked[txHash] ?? false): "Same unlock txHash has been executed" + } + self.allowedAmount = self.allowedAmount - amount + + TeleportCustodyAptos.unlocked[txHash] = true + emit Unlocked(amount: amount, from: from, txHash: txHash) + + let vault <- TeleportCustodyAptos.lockVault.withdraw(amount: amount) + let fee <- vault.withdraw(amount: self.unlockFee) + + self.feeCollector.deposit(from: <-fee) + emit FeeCollected(amount: self.unlockFee, type: 1) + + return <- vault + } + + pub fun withdrawFee(amount: UFix64): @FungibleToken.Vault { + return <- self.feeCollector.withdraw(amount: amount) + } + + pub fun updateLockFee(fee: UFix64) { + self.lockFee = fee + } + + pub fun updateUnlockFee(fee: UFix64) { + self.unlockFee = fee + } + + pub fun getFeeAmount(): UFix64 { + return self.feeCollector.balance + } + + pub fun depositAllowance(from: @Allowance) { + self.allowedAmount = self.allowedAmount + from.balance + + destroy from + } + + init(allowedAmount: UFix64) { + self.allowedAmount = allowedAmount + + self.feeCollector <- BloctoToken.createEmptyVault() as! @BloctoToken.Vault + self.lockFee = 3.0 + self.unlockFee = 0.01 + } + + destroy() { + destroy self.feeCollector + } + } + + pub fun getLockVaultBalance(): UFix64 { + return TeleportCustodyAptos.lockVault.balance + } + + init() { + // Aptos address length + self.teleportAddressLength = 64 + + // Aptos tx hash length + self.teleportTxHashLength = 64 + + self.AdminStoragePath = /storage/teleportCustodyAptosAdmin + self.TeleportAdminStoragePath = /storage/teleportCustodyAptosTeleportAdmin + self.TeleportAdminTeleportUserPath = /public/teleportCustodyAptosTeleportUser + self.TeleportAdminTeleportControlPath = /private/teleportCustodyAptosTeleportControl + + self.isFrozen = false + self.unlocked = {} + self.lockVault <- BloctoToken.createEmptyVault() as! @BloctoToken.Vault + + let admin <- create Administrator() + self.account.save(<-admin, to: self.AdminStoragePath) + } +} diff --git a/flow.json b/flow.json index 00da2f5..a5d1fa6 100644 --- a/flow.json +++ b/flow.json @@ -27,7 +27,8 @@ "BloctoToken": { "source": "./contracts/flow/token/BloctoToken.cdc", "aliases": { - "mainnet": "0x0f9df91c9121c460" + "mainnet": "0x0f9df91c9121c460", + "testnet": "0x6e0797ac987005f5" } }, "BloctoTokenMining": "./contracts/flow/mining/BloctoTokenMining.cdc", @@ -85,6 +86,13 @@ "testnet": "0x967a0fb3c949cbc5" } }, + "TeleportCustodyAptos": { + "source": "./contracts/flow/teleport/TeleportCustodyAptos.cdc", + "aliases": { + "mainnet": "TBD", + "testnet": "TBD" + } + }, "TeleportCustodySolana": { "source": "./contracts/flow/teleport/TeleportCustodySolana.cdc", "aliases": { @@ -346,6 +354,12 @@ ], "blt-swap-testnet": [ "BltUsdtSwapPair" + ], + "blt-teleport-testnet": [ + "TeleportCustodyEthereum", + "TeleportCustodyBSC", + "TeleportCustodySolana", + "TeleportCustodyAptos" ] }, "mainnet": { diff --git a/transactions/teleport/burnTokensAptos.cdc b/transactions/teleport/burnTokensAptos.cdc new file mode 100644 index 0000000..6c108c7 --- /dev/null +++ b/transactions/teleport/burnTokensAptos.cdc @@ -0,0 +1,23 @@ +import FungibleToken from "../../contracts/flow/token/FungibleToken.cdc" +import BloctoToken from "../../contracts/flow/token/BloctoToken.cdc" +import TeleportCustodyAptos from "../../contracts/flow/teleport/TeleportCustodyAptos.cdc" + +transaction(amount: UFix64, from: String, hash: String) { + + // The TeleportControl reference for teleport operations + let teleportControlRef: &TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportControl} + + prepare(teleportAdmin: AuthAccount) { + self.teleportControlRef = teleportAdmin.getCapability(TeleportCustodyAptos.TeleportAdminTeleportControlPath) + .borrow<&TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportControl}>() + ?? panic("Could not borrow a reference to TeleportControl") + } + + execute { + self.teleportControlRef.updateUnlockFee(fee: 0.0) + let vault <- self.teleportControlRef.unlock(amount: amount, from: from.decodeHex(), txHash: hash) + self.teleportControlRef.updateUnlockFee(fee: 0.1) + + destroy vault + } +} diff --git a/transactions/teleport/collectFeeAptos.cdc b/transactions/teleport/collectFeeAptos.cdc new file mode 100644 index 0000000..dc5306e --- /dev/null +++ b/transactions/teleport/collectFeeAptos.cdc @@ -0,0 +1,24 @@ +import FungibleToken from "../../contracts/flow/token/FungibleToken.cdc" +import TeleportCustodyAptos from "../../contracts/flow/teleport/TeleportCustodyAptos.cdc" +import BloctoToken from "../../contracts/flow/token/BloctoToken.cdc" + +transaction(to: Address) { + prepare(signer: AuthAccount) { + let adminRef = signer.borrow<&TeleportCustodyAptos.TeleportAdmin>(from: TeleportCustodyAptos.TeleportAdminStoragePath) + ?? panic("Could not borrow a reference to the admin resource") + + let feeAmount = adminRef.getFeeAmount(); + let vault <- adminRef.withdrawFee(amount: feeAmount); + + // Get the recipient's public account object + let recipient = getAccount(to) + + // Get a reference to the recipient's Receiver + let receiverRef = recipient.getCapability(BloctoToken.TokenPublicReceiverPath) + .borrow<&{FungibleToken.Receiver}>() + ?? panic("Could not borrow receiver reference to the recipient's Vault") + + // Deposit the withdrawn tokens in the recipient's receiver + receiverRef.deposit(from: <-vault) + } +} diff --git a/transactions/teleport/createTeleportAdminAptos.cdc b/transactions/teleport/createTeleportAdminAptos.cdc new file mode 100644 index 0000000..5cff015 --- /dev/null +++ b/transactions/teleport/createTeleportAdminAptos.cdc @@ -0,0 +1,23 @@ +import TeleportCustodyAptos from "../../contracts/flow/teleport/TeleportCustodyAptos.cdc" + +transaction(allowedAmount: UFix64) { + + prepare(admin: AuthAccount, teleportAdmin: AuthAccount) { + let adminRef = admin.borrow<&TeleportCustodyAptos.Administrator>(from: TeleportCustodyAptos.AdminStoragePath) + ?? panic("Could not borrow a reference to the admin resource") + + let teleportAdminResource <- adminRef.createNewTeleportAdmin(allowedAmount: allowedAmount) + + teleportAdmin.save(<- teleportAdminResource, to: TeleportCustodyAptos.TeleportAdminStoragePath) + + teleportAdmin.link<&TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportUser}>( + TeleportCustodyAptos.TeleportAdminTeleportUserPath, + target: TeleportCustodyAptos.TeleportAdminStoragePath + ) + + teleportAdmin.link<&TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportControl}>( + TeleportCustodyAptos.TeleportAdminTeleportControlPath, + target: TeleportCustodyAptos.TeleportAdminStoragePath + ) + } +} diff --git a/transactions/teleport/depositAllowanceAptos.cdc b/transactions/teleport/depositAllowanceAptos.cdc new file mode 100644 index 0000000..ca86556 --- /dev/null +++ b/transactions/teleport/depositAllowanceAptos.cdc @@ -0,0 +1,17 @@ +import TeleportCustodyAptos from "../../contracts/flow/teleport/TeleportCustodyAptos.cdc" + +transaction(teleportAdmin: Address, allowedAmount: UFix64) { + prepare(admin: AuthAccount) { + + let adminRef = admin.borrow<&TeleportCustodyAptos.Administrator>(from: TeleportCustodyAptos.AdminStoragePath) + ?? panic("Could not borrow a reference to the admin resource") + + let allowance <- adminRef.createAllowance(allowedAmount: allowedAmount) + + let teleportUserRef = getAccount(teleportAdmin).getCapability(TeleportCustodyAptos.TeleportAdminTeleportUserPath)! + .borrow<&TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportUser}>() + ?? panic("Could not borrow a reference to TeleportUser") + + teleportUserRef.depositAllowance(from: <- allowance) + } +} diff --git a/transactions/teleport/lockTokensAptos.cdc b/transactions/teleport/lockTokensAptos.cdc new file mode 100644 index 0000000..09dd6e7 --- /dev/null +++ b/transactions/teleport/lockTokensAptos.cdc @@ -0,0 +1,27 @@ +import FungibleToken from "../../contracts/flow/token/FungibleToken.cdc" +import BloctoToken from "../../contracts/flow/token/BloctoToken.cdc" +import TeleportCustodyAptos from "../../contracts/flow/teleport/TeleportCustodyAptos.cdc" + +transaction(admin: Address, amount: UFix64, target: String, toAddressType: String) { + + // The TeleportUser reference for teleport operations + let teleportUserRef: &TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportUser} + + // The Vault resource that holds the tokens that are being transferred + let sentVault: @FungibleToken.Vault + + prepare(signer: AuthAccount) { + self.teleportUserRef = getAccount(admin).getCapability(TeleportCustodyAptos.TeleportAdminTeleportUserPath) + .borrow<&TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportUser}>() + ?? panic("Could not borrow a reference to TeleportOut") + + let vaultRef = signer.borrow<&BloctoToken.Vault>(from: BloctoToken.TokenStoragePath) + ?? panic("Could not borrow a reference to the vault resource") + + self.sentVault <- vaultRef.withdraw(amount: amount) + } + + execute { + self.teleportUserRef.lock(from: <- self.sentVault, to: target.decodeHex(), toAddressType: toAddressType) + } +} diff --git a/transactions/teleport/transferTeleportFeesAptos.cdc b/transactions/teleport/transferTeleportFeesAptos.cdc new file mode 100644 index 0000000..3eecfc2 --- /dev/null +++ b/transactions/teleport/transferTeleportFeesAptos.cdc @@ -0,0 +1,28 @@ +import FungibleToken from "../../contracts/flow/token/FungibleToken.cdc" +import BloctoToken from "../../contracts/flow/token/BloctoToken.cdc" +import TeleportCustodyAptos from "../../contracts/flow/teleport/TeleportCustodyAptos.cdc" + +transaction(target: Address) { + // The teleport admin reference + let teleportAdminRef: &TeleportCustodyAptos.TeleportAdmin + + prepare(teleportAdmin: AuthAccount) { + self.teleportAdminRef = teleportAdmin.borrow<&TeleportCustodyAptos.TeleportAdmin>(from: TeleportCustodyAptos.TeleportAdminStoragePath) + ?? panic("Could not borrow a reference to the teleport admin resource") + } + + execute { + let feeVault <- self.teleportAdminRef.withdrawFee(amount: self.teleportAdminRef.getFeeAmount()) + + // Get the recipient's public account object + let recipient = getAccount(target) + + // Get a reference to the recipient's Receiver + let receiverRef = recipient.getCapability(BloctoToken.TokenPublicReceiverPath) + .borrow<&{FungibleToken.Receiver}>() + ?? panic("Could not borrow receiver reference to the recipient's Vault") + + // Deposit the withdrawn tokens in the recipient's receiver + receiverRef.deposit(from: <- feeVault) + } +} diff --git a/transactions/teleport/unlockTokensAptos.cdc b/transactions/teleport/unlockTokensAptos.cdc new file mode 100644 index 0000000..6ca64cd --- /dev/null +++ b/transactions/teleport/unlockTokensAptos.cdc @@ -0,0 +1,28 @@ +import FungibleToken from "../../contracts/flow/token/FungibleToken.cdc" +import BloctoToken from "../../contracts/flow/token/BloctoToken.cdc" +import TeleportCustodyAptos from "../../contracts/flow/teleport/TeleportCustodyAptos.cdc" + +transaction(amount: UFix64, target: Address, from: String, hash: String) { + + // The TeleportControl reference for teleport operations + let teleportControlRef: &TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportControl} + + // The Receiver reference of the user + let receiverRef: &BloctoToken.Vault{FungibleToken.Receiver} + + prepare(teleportAdmin: AuthAccount) { + self.teleportControlRef = teleportAdmin.getCapability(TeleportCustodyAptos.TeleportAdminTeleportControlPath) + .borrow<&TeleportCustodyAptos.TeleportAdmin{TeleportCustodyAptos.TeleportControl}>() + ?? panic("Could not borrow a reference to TeleportControl") + + self.receiverRef = getAccount(target).getCapability(BloctoToken.TokenPublicReceiverPath) + .borrow<&BloctoToken.Vault{FungibleToken.Receiver}>() + ?? panic("Could not borrow a reference to Receiver") + } + + execute { + let vault <- self.teleportControlRef.unlock(amount: amount, from: from.decodeHex(), txHash: hash) + + self.receiverRef.deposit(from: <- vault) + } +} diff --git a/transactions/teleport/updateTeleportFeesAptos.cdc b/transactions/teleport/updateTeleportFeesAptos.cdc new file mode 100644 index 0000000..830293b --- /dev/null +++ b/transactions/teleport/updateTeleportFeesAptos.cdc @@ -0,0 +1,11 @@ +import TeleportCustodyAptos from "../../contracts/flow/teleport/TeleportCustodyAptos.cdc" + +transaction(lockFee: UFix64, unlockFee: UFix64) { + prepare(teleportAdmin: AuthAccount) { + let teleportAdminRef = teleportAdmin.borrow<&TeleportCustodyAptos.TeleportAdmin>(from: TeleportCustodyAptos.TeleportAdminStoragePath) + ?? panic("Could not borrow a reference to the teleport admin resource") + + teleportAdminRef.updateLockFee(fee: lockFee) + teleportAdminRef.updateUnlockFee(fee: unlockFee) + } +}