Skip to content

Commit

Permalink
Withdraw from Sapphire to Consensus
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaw3d committed Jun 26, 2024
1 parent 426dcdc commit f28a420
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 52 deletions.
17 changes: 8 additions & 9 deletions src/index.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
<pre>
Keep this page opened until tokens arrive at your Sapphire account.
Keep this page opened until tokens arrive at your Consensus account.

Save the generated temporary mnemonic - you'll need it if the page refreshes or device crashes. Or if you send tokens to the account by mistake at a later date.
Save the generated temporary private key - you'll need it if the page refreshes or device crashes. Or if you send tokens to the account by mistake at a later date.

Generated a temporary mnemonic:
<span id="print_mnemonic"></span>
(using first account, at derivation path: m/44'/474'/0')
Generated a temporary private key:
<span id="print_privatekey"></span>

Send a small amount of ROSE tokens to this Consensus account:
<span id="print_consensus_account"></span>

Forwarding received ROSE to Sapphire account:
Send a small amount of ROSE tokens to this Sapphire account:
<span id="print_sapphire_account"></span>

Forwarding received ROSE to Consensus account:
<span id="print_consensus_account"></span>
</pre>

<script type="module" src="./index.mjs"></script>
86 changes: 43 additions & 43 deletions src/index.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-check
import * as oasis from '@oasisprotocol/client'
import * as oasisRT from '@oasisprotocol/client-rt'
import { bytesToHex, privateToAddress, toChecksumAddress } from '@ethereumjs/util'

const sapphireConfig = {
mainnet: {
Expand All @@ -21,71 +22,54 @@ const consensusConfig = {
const multiplyConsensusToSapphire = 10n ** BigInt(sapphireConfig.decimals - consensusConfig.decimals)

async function init() {
const mnemonic = oasis.hdkey.HDKey.generateMnemonic(256)
const signerKeyPair = await oasis.hdkey.HDKey.getAccountSigner(mnemonic, 0)
const signer = oasis.signature.NaclSigner.fromSecret(signerKeyPair.secretKey, 'this key is not important')
const signer = oasisRT.signatureSecp256k1.EllipticSigner.fromRandom('this key is not important')
const sapphireAddress = privateToEthAddress(signer.key.getPrivate('hex'))

const consensusAddress =
/** @type {`oasis1${string}`} */
(await publicKeyToAddress(signerKeyPair.publicKey))
(prompt('Consensus address you want to send ROSE to', 'oasis1') || '')
if (!isValidOasisAddress(consensusAddress)) throw new Error('Invalid consensus address')

const sapphireAddress =
/** @type {`0x${string}`} */
(prompt('Sapphire address you want to send ROSE to', '0x') || '')
if (!sapphireAddress) throw new Error('Invalid sapphire address')
if (!/^0x[0-9a-fA-F]{40}$/.test(sapphireAddress)) throw new Error('Invalid sapphire address')

const nic = new oasis.client.NodeInternal('https://grpc.oasis.io')
const chainContext = await nic.consensusGetChainContext()

async function poll() {
try {
const consensusBalance = await getConsensusBalance(consensusAddress)
const sapphireBalance = await getSapphireBalance(sapphireAddress)
console.log({ consensusBalance, sapphireBalance })
const consensusBalance = await getConsensusBalance(consensusAddress)
console.log({ sapphireBalance, consensusBalance })

window.print_mnemonic.textContent = mnemonic
window.print_consensus_account.textContent = consensusAddress + ' balance: ' + consensusBalance
window.print_privatekey.textContent = signer.key.getPrivate('hex')
window.print_sapphire_account.textContent = sapphireAddress + ' balance: ' + sapphireBalance
if (consensusBalance <= 0n) {
window.print_consensus_account.textContent = consensusAddress + ' balance: ' + consensusBalance
if (sapphireBalance <= 0n) {
setTimeout(poll, 10000)
return
}

console.log('depositable', consensusBalance)
const amountToDeposit = consensusBalance

// setAllowance to sapphireConfig.mainnet.address
const tw = oasis.staking.allowWrapper()
tw.setNonce(await getConsensusNonce(consensusAddress))
tw.setFeeAmount(oasis.quantity.fromBigInt(0n))
tw.setBody({
beneficiary: oasis.staking.addressFromBech32(sapphireConfig.mainnet.address),
negative: false,
amount_change: oasis.quantity.fromBigInt(amountToDeposit), // TODO: this assumes that initial allowance is 0
})
const gas = await tw.estimateGas(nic, signer.public())
tw.setFeeGas(gas)
await tw.sign(new oasis.signature.BlindContextSigner(signer), chainContext)
await tw.submit(nic)

// Deposit into Sapphire
console.log('withdrawable', sapphireBalance)
const feeAmount = sapphireConfig.gasPrice * sapphireConfig.feeGas * multiplyConsensusToSapphire
const amountToWithdraw = sapphireBalance - feeAmount

// Withdraw into Sapphire
const rtw = new oasisRT.consensusAccounts.Wrapper(
oasis.misc.fromHex(sapphireConfig.mainnet.runtimeId),
).callDeposit()
).callWithdraw()
rtw
.setBody({
amount: [oasis.quantity.fromBigInt(amountToDeposit * multiplyConsensusToSapphire), oasisRT.token.NATIVE_DENOMINATION],
to: oasis.staking.addressFromBech32(await getEvmBech32Address(sapphireAddress)),
amount: [oasis.quantity.fromBigInt(amountToWithdraw), oasisRT.token.NATIVE_DENOMINATION],
to: oasis.staking.addressFromBech32(consensusAddress),
})
.setFeeAmount([oasis.quantity.fromBigInt(0n), oasisRT.token.NATIVE_DENOMINATION])
.setFeeAmount([oasis.quantity.fromBigInt(feeAmount), oasisRT.token.NATIVE_DENOMINATION])
.setFeeGas(sapphireConfig.feeGas)
.setFeeConsensusMessages(1)
.setSignerInfo([
{
address_spec: {
signature: { ed25519: signer.public() },
signature: { secp256k1eth: signer.public() },
},
nonce: await getSapphireNonce(consensusAddress),
nonce: await getSapphireNonce(await getEvmBech32Address(sapphireAddress)),
},
])
await rtw.sign([new oasis.signature.BlindContextSigner(signer)], chainContext)
Expand All @@ -112,10 +96,26 @@ init().catch((err) => {

// Utils

/** @param {Uint8Array} publicKey */
async function publicKeyToAddress(publicKey) {
const data = await oasis.staking.addressFromPublicKey(publicKey)
return oasis.staking.addressToBech32(data)
/** @param {`oasis1${string}`} oasisAddress */
function isValidOasisAddress(oasisAddress) {
try {
oasis.staking.addressFromBech32(oasisAddress)
return true
} catch (e) {
return false
}
}

/** @param {string} ethPrivateKey */
function privateToEthAddress(ethPrivateKey) {
return /** @type {`0x${string}`} */ (
toChecksumAddress(bytesToHex(privateToAddress(hexToBuffer(ethPrivateKey))))
)
}

/** @param {string} value */
function hexToBuffer(value) {
return Buffer.from(value, 'hex')
}

/** @param {`0x${string}`} evmAddress */
Expand All @@ -126,7 +126,7 @@ async function getEvmBech32Address(evmAddress) {
oasisRT.address.V0_SECP256K1ETH_CONTEXT_VERSION,
evmBytes,
)
const bech32Address = oasisRT.address.toBech32(address)
const bech32Address = /** @type {`oasis1${string}`}*/ (oasisRT.address.toBech32(address))
return bech32Address
}

Expand Down

0 comments on commit f28a420

Please sign in to comment.