Advertisement
Akuukis

FIC Network - FIC smart contract 1/2

Jul 11th, 2018
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const cfg = require('../config/config');
  2. import {Server,Network,Memo,Keypair,Operation,TransactionBuilder} from 'stellar-sdk';
  3.  
  4.  
  5. const horizon = new Server(cfg.stellar.providerUrl);  // Endpoint for internally hosted horizon.
  6. Network.use(new Network(cfg.stellar.networkPassphrase))  // Either "FIC Network ; 2018" or "FIC Test Network ; 2017".
  7.  
  8. // Constants
  9. const BASE_DEPOSIT = 200;
  10. const MIN_AMOUNT = BASE_DEPOSIT * 6;
  11. const TX_FEE = 0.00001;
  12.  
  13. // Mainnet constants.
  14. // const LOCKUP_START = 1526425200;  // May 15
  15. // const LOCKUP_90_DATE = LOCKUP_START + (60 * 60 * 24) * 90; // Mon, 13 Aug 2018 23:00:00 +0000
  16. // const LOCKUP_180_DATE = LOCKUP_START + (60 * 60 * 24) * 180; // Sun, 11 Nov 2018 23:00:00 +0000
  17.  
  18. // Testnet constants.
  19. const LOCKUP_START = 1531116000;  // Mon, 09 Jul 2018 6:00:00 +0000
  20. const LOCKUP_90_DATE = LOCKUP_START + (60 * 60 * 24) * 5; // Fri, 14 Jul 2018 6:00:00 +0000
  21. const LOCKUP_180_DATE = LOCKUP_START + (60 * 60 * 24) * 10; // Wed, 19 Jul 2018 6:00:00 +0000
  22.  
  23. // Helpers
  24. const submit = (te, msg) => horizon.submitTransaction(te).catch(e => e).then(res => console.log(msg, res))
  25.  
  26. export default class FicContract {
  27.     constructor(secret) {
  28.         this.keypair = Keypair.fromSecret(secret)
  29.  
  30.     }
  31.     async init() {
  32.         this.account = await horizon.loadAccount(this.keypair.publicKey())
  33.     }
  34.  
  35.     async transfer(userAddress, amount, lockupPeriod, ethAddress, ethTxHash) {
  36.         console.info(`\n\nProcessing withdraw request from ${userAddress} of ${lockupPeriod}-day ${amount} FICs..`);
  37.  
  38.  
  39.         // 1. Parse input
  40.         if (amount < MIN_AMOUNT) throw new Error(`Minimum amount is ${MIN_AMOUNT} FIC`)
  41.         let minTime;
  42.         let lockupCode;
  43.         switch (lockupPeriod) {
  44.             case (0):
  45.                 {
  46.                     minTime = String(LOCKUP_START);
  47.                     lockupCode = 0x00;
  48.                     break;
  49.                 }
  50.             case (90):
  51.                 {
  52.                     minTime = String(LOCKUP_90_DATE);
  53.                     lockupCode = 0x01;
  54.                     break;
  55.                 }
  56.             case (180):
  57.                 {
  58.                     minTime = String(LOCKUP_180_DATE);
  59.                     lockupCode = 0x02;
  60.                     break;
  61.                 }
  62.             default:
  63.                 {
  64.                     throw new Error('Bad lockup period')
  65.                 }
  66.         }
  67.         const no = (new Array(7)).fill(0).map(()=>String.fromCharCode(Math.floor(48+Math.random()*(122-48))));
  68.         const uid = `${userAddress}_${no.join('')}`;
  69.  
  70.  
  71.         // 2. Select lockup keypair.
  72.         const lockupKp = Keypair.random(); // Lockup account that escrows user's funds, unique per user per lockup.
  73.  
  74.         // 3. Create and fund lockup account.
  75.         const te1 = new TransactionBuilder(this.account)
  76.             .addMemo(Memo.hash(Buffer.concat([
  77.                     Buffer.from(ethAddress.slice(2, 42), 'hex'),  // Save withdraw requester's ethereum address.
  78.                     Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, lockupCode]),  // Save lockup period.
  79.                 ])))
  80.             .addOperation(Operation.createAccount({
  81.                 source: this.keypair.publicKey(),
  82.                 destination: lockupKp.publicKey(),
  83.                 startingBalance: String(amount + 2 * TX_FEE), // prepay presigned transactions.
  84.             }))
  85.             .addOperation(Operation.manageData({
  86.                 source: this.keypair.publicKey(),
  87.                 name: uid,
  88.                 value: `${lockupKp.publicKey()}`,
  89.             }))
  90.             .build();
  91.         te1.sign(this.keypair);
  92.         await submit(te1, `Create lockup account ${lockupKp.publicKey()}:`);
  93.  
  94.         const lockupAccount = await horizon.loadAccount(lockupKp.publicKey());
  95.         console.log(`Lockup account ${lockupKp.publicKey()} sequence & balance:`, lockupAccount.sequenceNumber(), lockupAccount.balances)
  96.  
  97.         // 4. Setup presigned transactions.
  98.         // - pre-authorize tx (`pretx1`) that will merge all funds into user.
  99.         // - pre-authorize tx (`pretx2`) that will create user in case it isn't already.
  100.         // - obviously setup timebounds (commented out for testing)
  101.         const prete1 = new TransactionBuilder(lockupAccount, { timebounds: {minTime, maxTime: Number.MAX_SAFE_INTEGER} })
  102.             .addOperation(Operation.createAccount({
  103.                 source: lockupKp.publicKey(),
  104.                 destination: userAddress,
  105.                 startingBalance: String(MIN_AMOUNT),
  106.             }))
  107.             .build(); // FYI `build()` also increments sequence within `lockupAccount`.
  108.         const prete2 = new TransactionBuilder(lockupAccount, { timebounds: {minTime, maxTime: Number.MAX_SAFE_INTEGER} })
  109.             .addOperation(Operation.accountMerge({
  110.                 source: lockupKp.publicKey(),
  111.                 destination: userAddress,
  112.             }))
  113.             .build();
  114.  
  115.         console.info('PreTx1:', prete1.toEnvelope().toXDR().toString('base64'));
  116.         console.info('PreTx2:', prete2.toEnvelope().toXDR().toString('base64'));
  117.  
  118.  
  119.         // 5. Setup lockup account by removing master key, effectively locking lockup account.
  120.         const te2 = new TransactionBuilder(this.account)
  121.             .addMemo(Memo.hash(ethTxHash.slice(2, 66)))  // Save withdraw request's tx hash here.
  122.             .addOperation(Operation.setOptions({
  123.                 source: lockupKp.publicKey(),
  124.                 signer: {
  125.                     preAuthTx: prete1.hash(),
  126.                     weight: '1'
  127.                 },
  128.             }))
  129.             .addOperation(Operation.setOptions({
  130.                 source: lockupKp.publicKey(),
  131.                 masterWeight: '0',
  132.                 signer: {
  133.                     preAuthTx: prete2.hash(),
  134.                     weight: '1'
  135.                 },
  136.             }))
  137.             .build();
  138.         te2.sign(this.keypair);
  139.         te2.sign(lockupKp);
  140.  
  141.         await submit(te2, `Setup lockup account ${lockupKp.publicKey()}:`);
  142.  
  143.  
  144.         // 6. A lot later user submits both pre-authorized transaction envelopes to claim FIC coins.
  145.         // - Those envelopes are recreate-able from publicly available information saved in two transactions above.
  146.         // - The lockup account itself is lookup-able in network on `this.account` data fields.
  147.         return async () => {
  148.             await submit(prete1, `Create user account (${userAddress}):`);
  149.             await submit(prete2, `Merge into user account (${userAddress}):`);
  150.  
  151.             const userAccount = await horizon.loadAccount(userAddress);
  152.             console.log(`User account (${userAddress}) balance:`, userAccount.balances)
  153.         }
  154.     }
  155. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement