Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const cfg = require('../config/config');
- import {Server,Network,Memo,Keypair,Operation,TransactionBuilder} from 'stellar-sdk';
- const horizon = new Server(cfg.stellar.providerUrl); // Endpoint for internally hosted horizon.
- Network.use(new Network(cfg.stellar.networkPassphrase)) // Either "FIC Network ; 2018" or "FIC Test Network ; 2017".
- // Constants
- const BASE_DEPOSIT = 200;
- const MIN_AMOUNT = BASE_DEPOSIT * 6;
- const TX_FEE = 0.00001;
- // Mainnet constants.
- // const LOCKUP_START = 1526425200; // May 15
- // const LOCKUP_90_DATE = LOCKUP_START + (60 * 60 * 24) * 90; // Mon, 13 Aug 2018 23:00:00 +0000
- // const LOCKUP_180_DATE = LOCKUP_START + (60 * 60 * 24) * 180; // Sun, 11 Nov 2018 23:00:00 +0000
- // Testnet constants.
- const LOCKUP_START = 1531116000; // Mon, 09 Jul 2018 6:00:00 +0000
- const LOCKUP_90_DATE = LOCKUP_START + (60 * 60 * 24) * 5; // Fri, 14 Jul 2018 6:00:00 +0000
- const LOCKUP_180_DATE = LOCKUP_START + (60 * 60 * 24) * 10; // Wed, 19 Jul 2018 6:00:00 +0000
- // Helpers
- const submit = (te, msg) => horizon.submitTransaction(te).catch(e => e).then(res => console.log(msg, res))
- export default class FicContract {
- constructor(secret) {
- this.keypair = Keypair.fromSecret(secret)
- }
- async init() {
- this.account = await horizon.loadAccount(this.keypair.publicKey())
- }
- async transfer(userAddress, amount, lockupPeriod, ethAddress, ethTxHash) {
- console.info(`\n\nProcessing withdraw request from ${userAddress} of ${lockupPeriod}-day ${amount} FICs..`);
- // 1. Parse input
- if (amount < MIN_AMOUNT) throw new Error(`Minimum amount is ${MIN_AMOUNT} FIC`)
- let minTime;
- let lockupCode;
- switch (lockupPeriod) {
- case (0):
- {
- minTime = String(LOCKUP_START);
- lockupCode = 0x00;
- break;
- }
- case (90):
- {
- minTime = String(LOCKUP_90_DATE);
- lockupCode = 0x01;
- break;
- }
- case (180):
- {
- minTime = String(LOCKUP_180_DATE);
- lockupCode = 0x02;
- break;
- }
- default:
- {
- throw new Error('Bad lockup period')
- }
- }
- const no = (new Array(7)).fill(0).map(()=>String.fromCharCode(Math.floor(48+Math.random()*(122-48))));
- const uid = `${userAddress}_${no.join('')}`;
- // 2. Select lockup keypair.
- const lockupKp = Keypair.random(); // Lockup account that escrows user's funds, unique per user per lockup.
- // 3. Create and fund lockup account.
- const te1 = new TransactionBuilder(this.account)
- .addMemo(Memo.hash(Buffer.concat([
- Buffer.from(ethAddress.slice(2, 42), 'hex'), // Save withdraw requester's ethereum address.
- Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, lockupCode]), // Save lockup period.
- ])))
- .addOperation(Operation.createAccount({
- source: this.keypair.publicKey(),
- destination: lockupKp.publicKey(),
- startingBalance: String(amount + 2 * TX_FEE), // prepay presigned transactions.
- }))
- .addOperation(Operation.manageData({
- source: this.keypair.publicKey(),
- name: uid,
- value: `${lockupKp.publicKey()}`,
- }))
- .build();
- te1.sign(this.keypair);
- await submit(te1, `Create lockup account ${lockupKp.publicKey()}:`);
- const lockupAccount = await horizon.loadAccount(lockupKp.publicKey());
- console.log(`Lockup account ${lockupKp.publicKey()} sequence & balance:`, lockupAccount.sequenceNumber(), lockupAccount.balances)
- // 4. Setup presigned transactions.
- // - pre-authorize tx (`pretx1`) that will merge all funds into user.
- // - pre-authorize tx (`pretx2`) that will create user in case it isn't already.
- // - obviously setup timebounds (commented out for testing)
- const prete1 = new TransactionBuilder(lockupAccount, { timebounds: {minTime, maxTime: Number.MAX_SAFE_INTEGER} })
- .addOperation(Operation.createAccount({
- source: lockupKp.publicKey(),
- destination: userAddress,
- startingBalance: String(MIN_AMOUNT),
- }))
- .build(); // FYI `build()` also increments sequence within `lockupAccount`.
- const prete2 = new TransactionBuilder(lockupAccount, { timebounds: {minTime, maxTime: Number.MAX_SAFE_INTEGER} })
- .addOperation(Operation.accountMerge({
- source: lockupKp.publicKey(),
- destination: userAddress,
- }))
- .build();
- console.info('PreTx1:', prete1.toEnvelope().toXDR().toString('base64'));
- console.info('PreTx2:', prete2.toEnvelope().toXDR().toString('base64'));
- // 5. Setup lockup account by removing master key, effectively locking lockup account.
- const te2 = new TransactionBuilder(this.account)
- .addMemo(Memo.hash(ethTxHash.slice(2, 66))) // Save withdraw request's tx hash here.
- .addOperation(Operation.setOptions({
- source: lockupKp.publicKey(),
- signer: {
- preAuthTx: prete1.hash(),
- weight: '1'
- },
- }))
- .addOperation(Operation.setOptions({
- source: lockupKp.publicKey(),
- masterWeight: '0',
- signer: {
- preAuthTx: prete2.hash(),
- weight: '1'
- },
- }))
- .build();
- te2.sign(this.keypair);
- te2.sign(lockupKp);
- await submit(te2, `Setup lockup account ${lockupKp.publicKey()}:`);
- // 6. A lot later user submits both pre-authorized transaction envelopes to claim FIC coins.
- // - Those envelopes are recreate-able from publicly available information saved in two transactions above.
- // - The lockup account itself is lookup-able in network on `this.account` data fields.
- return async () => {
- await submit(prete1, `Create user account (${userAddress}):`);
- await submit(prete2, `Merge into user account (${userAddress}):`);
- const userAccount = await horizon.loadAccount(userAddress);
- console.log(`User account (${userAddress}) balance:`, userAccount.balances)
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement