casper-network / docs

Application running the documentation markdown files.
Apache License 2.0
25 stars 65 forks source link

Developer workflow for integrating staking commands #659

Closed ipopescu closed 2 years ago

ipopescu commented 2 years ago

Document the developer workflow for integrating staking commands under: https://docs.casperlabs.io/dapp-dev-guide/list-cspr/

Uphold (the crypto exchange) is integrating more tightly with Casper. They want to offer their custodial users the ability to stake/unstake from uphold.com etc., and are trying to figure out the implementation, for which no good docs exist at the moment. Since Make services have built this for CSPR.live, they offered help. Please connect with them to document the following:

eugenebelov commented 2 years ago

The delegate / undelegate / redelegate deploy structure and parameters

In general, all 3 operations are consists of two parts: creating deploy object and sign it with wallet (Signer, Ledger, etc.) Following information are required:

1. Preparing deploy before signed in:

To make a deploy, we need to have deployParams, session and a payment. Deploy params is an DeployUtil.DeployParams object from delegator publicKey and network name.

import { DeployUtil, CLPublicKey } from 'casper-js-sdk';

const deployParams = new DeployUtil.DeployParams(
  CLPublicKey.fromHex(publicKeyHex),
  network_name // 'testnet' | 'mainnet'
);

For creating a session object, which is DeployUtil.ExecutableDeployItem, we need delegator and validator public keys, operation amount, auction manager contract hash, and entry point.

At first, create a variable RuntimeArgs from public keys and amount, we need to use it below in session:

import { RuntimeArgs, CLValueBuilder, CLPublicKey } from 'casper-js-sdk';

const args = RuntimeArgs.fromMap({
  delegator: CLPublicKey.fromHex(delegatorPublicKeyHex),
  validator: CLPublicKey.fromHex(validatorPublicKeyHex),
  amount: CLValueBuilder.u512(amountMotes) // in motes
});

Second, create a session parameter. It consists of Uint8Array auction manager contract hash, entry point's and runtime arguments, whuch was created before.

import { decodeBase16, DeployUtil } from 'casper-js-sdk';

const session = DeployUtil.ExecutableDeployItem.newStoredContractByHash(
  decodeBase16(auction_manager_contract_hash), // auction manager contract hash
  contractEntryPoint, // auction manager entry point
  args
);

To create payment parameter for deploy, we need a deploy cost. The actual costs could be pulled from the network chainspec, at the moment of writing the values are the following. Use DeployUtil.standardPayment method, for creating payment.

import { DeployUtil } from 'casper-js-sdk';

const payment = DeployUtil.standardPayment(deployCost);

The last operation is creating a deploy:

import { DeployUtil } from 'casper-js-sdk';

DeployUtil.makeDeploy(deployParams, session, payment);

Redelegation, has the same way as a delegation, but with introducing 3rd public_key.

import { RuntimeArgs, CLPublicKey, CLValueBuilder } from 'casper-js-sdk';

const args = RuntimeArgs.fromMap({
    delegator: CLPublicKey.fromHex(delegatorPublicKeyHex),
    validator: CLPublicKey.fromHex(validatorPublicKeyHex),
    new_validator: CLPublicKey.fromHex(redelegateValidatorPublicKeyHex),
    amount: CLValueBuilder.u512(amountMotes)
})

2. Sign and sent deploy

Auction manager entry points

Amount minimums and costs

The actual costs could be pulled from the network chainspec, at the moment of writing the values are the following:

transfer_cost: "100000000", // motes
delegate_cost: "2500000000", // motes
undelegate_cost: "10000", // motes

Transfers and delegation minimums are:

transfer_min_amount: "2500000000", // motes
delegation_min_amount: "500000000000", // motes

Current delegation cap, and the logic around being able to delegate more if you’re an existing delegator but not able to start a new delegation, if a validator is at the cap

Casper’s 1.4.5 upgrade includes a new Delegator limit rule, which sets a cap on the number of delegators a validator may have at 953. This is a temporary solution to prevent complications with Casper’s fast sync mechanism - in which high bond counts could break fast sync. Validators with a delegator count at or above 953 at the time of the 1.4.5 upgrade will be grandfathered in, however new delegators will not be able to delegate to any validator until the delegator count for that validator falls below 953.

For example, MAKE is a validator with 1000 delegators prior to the 1.4.5 upgrade. After the 1.4.5 upgrade, MAKE cannot accept any new delegators because their delegator count is above the cap (1000 > 953). However, a delegator that was staking with MAKE prior to the upgrade, is free to delegate more CSPR to MAKE if they so choose - the cap does not prevent existing delegators to up their stake. If enough delegators unbond from MAKE’s node, and their delegator count drops below 953, only then will a new delegator will be able to bond.

Auction contract addresses on various networks

 mainnet - ccb576d6ce6dec84a551e48f0d0b7af89ddba44c7390b690036257a04a3ae9ea
 testnet - 93d923e336b20a4c4ca14d592b60e5bd3fe330775618290104f9beb326db7ae2

Differences when signing a deploy with Signer and Ledger

Signer

To get signature, need to use Signer.sign from cusper-js-sdk. It will return Promise<{ deploy }>, which is signed object. Use DeployUtil.deployFromJson to convert the result and sent it to network with

import { Signer, CasperServiceByJsonRPC, DeployUtil } from 'casper-js-sdk';

const casperService = new CasperServiceByJsonRPC(GRPC_URL);
const deployJson = DeployUtil.deployToJson(deploy);
Signer.sign(
    deployJson,
    accountPublicKey,
    recipientPublicKey
).then((signedDeployJson) => {
    const signedDeploy = DeployUtil.deployFromJson(signedDeployJson);
    if (signedDeploy.ok) {
      casperService.deploy(signedDeploy.val! as DeployUtil.Deploy); // sent deploy
    }
}

Ledger

Need to connect with Ledger first and get get signature.

import TransportWebUSB from '@ledgerhq/hw-transport-webusb';
import LedgerApp, { ResponseBase } from '@zondax/ledger-casper';
import { DeployUtil } from 'casper-js-sdk';

const getBipPath = (index: number) => {
  const idx = index.toString();
  return `m/44'/506'/0'/0/${idx}`;
};

const deployBytes = DeployUtil.deployToBytes(deploy) as Buffer;
const transport = await TransportWebUSB.create();
const ledgerApp = new LedgerApp(transport);
const res = await ledgerApp.sign(
    getBipPath(selectedAccountIndex),
    deployBytes
);

Signature will be in a property res.signatureRS.

After that, we can create signed deploy

import { DeployUtil, CLPublicKey } from 'casper-js-sdk';

const signedDeploy = DeployUtil.setSignature(
  deploy,
  signatureRS,
  CLPublicKey.fromHex(accountPublicKey)
);

and sent it to network.

casperService.deploy(signedDeploy)
ACStone-MTS commented 2 years ago

Thank you @eugenebelov ! I'll definitely incorporate this into the documentation.

darthsiroftardis commented 2 years ago

Thanks for this content @eugenebelov we shall integrate into the content we were preparing. this helps us greatly

devendran-m commented 2 years ago

Thank you, @eugenebelov, for the valuable content.