Skip to content

POW Distribution

PoW or Proof-of-Work is a mechanism which allows SKALE chain owner to mine sufficient amount of sFUEL to any address to send transaction, if address doesn’t have enough amount of sFUEL.

PoW Usage

  1. The client generates a unique gasPrice value based on the transaction data
  2. The client sends transaction with a unique gasPrice value and the required gasAmount, which was calculated during the algorithm.
  3. GasPrice and gasAmount of this transaction will be checked on the SKALE chain side by the same check in the algorithm.
  4. Transaction is executed, even without sFUEL in the client’s account.

PoW algorithm

  1. The client generates a random 256-bit X by Formula which is equal a unique gasPrice value
X=sha3(address)sha3(nonce)sha3(gasPrice) freeGas=(2256−1)/X/difficulty
  1. Check whether X is allow to run transaction
freeGas(X)>estimateGas(tx)

a. If not, then back to the first step

b. If the condition is met - send the transaction with gasPrice = X and gasAmount = freeGas(X)

PoW sFUEL Distribution

  1. Get a wallet to sign transactions - a random generated one can be used since PoW based transactions don’t require signer to have sFUEL
  2. Create a transaction to call sFUEL distribution contract function
  3. Unique gasPrice function generated - based on: signer address, signer nonce and gas
  4. Send transaction with the unique GasPrice
Available On-Chain sFUEL Faucets
ChainMainnet AddressTestnet AddressFunction Signature
Europa0x2B267A3e49b351DEdac892400a530ABb2f899d230x366727B410fE55774C8b0B5b5A6E2d74199a088A0x0c11dedd
Calypso0x02891b34B7911A9C68e82C193cd7A6fBf0c3b30A0x62Fe932FF26e0087Ae383f6080bd2Ed481bA5A8A0x0c11dedd
Nebula0x5a6869ef5b81DCb58EBF51b8F893c31f5AFE3Fa80x000E9c53C4e2e21F5063f2e232d0AA907318dccb0x0c11dedd
Titan0xa5C297dF8f8386E4b940D61EF9A8f2bB367a6fAB0x08f98Af60eb83C18184231591A8F89577E46A4B90x0c11dedd

Implementation Example

  1. Random signer generation
    import { JsonRpcProvider, Wallet } from "ethers";
    const provider = new JsonRpcProvider(chain?.chainInfo?.[isMainnet ? "mainnet" : "testnet"]?.rpcUrl);
    const randomWallet = Wallet.createRandom(provider);
  2. Request creation to call sFUEL distribution contract function
    const nonce = await provider.getTransactionCount(randomWallet.address);
    const functionSignature = key === "europa" && isMainnet ? "0x6a627842" : "0x0c11dedd";
    const { duration, gasPrice } = await miner.mineGasForTransaction(nonce, 100_000, randomWallet.address);
    const request = {
    to: chain?.chainInfo?.[isMainnet ? "mainnet" : "testnet"].proofOfWork,
    data: `${functionSignature}000000000000000000000000${address.substring(2)}`,
    gasLimit: 100_000,
    gasPrice
    }
  3. Unique Gas Price Generation
    import { isHexString, getNumber, randomBytes, keccak256, toBeHex, toBigInt, ethers } from 'ethers'
    export default class Miner {
    public static MAX_NUMBER = ethers.MaxUint256;
    public async mineGasForTransaction(
    nonce: string | number,
    gas: string | number,
    from: string
    ): Promise<{
    duration: number;
    gasPrice: bigint;
    }> {
    let address = from
    nonce = isHexString(nonce) ? getNumber(nonce) : (nonce as number)
    gas = isHexString(gas) ? getNumber(gas) : (gas as number)
    return await this.mineFreeGas(gas as number, address, nonce as number)
    }
    public async mineFreeGas(gasAmount: number, address: string, nonce: number): Promise<{
    duration: number;
    gasPrice: bigint;
    }> {
    let nonceHash = toBigInt(keccak256(toBeHex(nonce, 32)));
    let addressHash = toBigInt(keccak256(address));
    let nonceAddressXOR = nonceHash ^ addressHash;
    let divConstant = Miner.MAX_NUMBER;
    let candidate: Uint8Array;
    let iterations = 0;
    const start = performance.now();
    while (true) {
    candidate = randomBytes(32)
    let candidateHash = toBigInt(keccak256(candidate));
    let resultHash = nonceAddressXOR ^ candidateHash;
    let externalGas = divConstant / resultHash;
    if (externalGas >= gasAmount) {
    break;
    }
    // every 2k iterations, yield to the event loop
    if (iterations++ % 1_000 === 0) {
    await new Promise<void>((resolve) => setTimeout(resolve, 0));
    }
    }
    const end = performance.now();
    return {
    duration: start - end,
    gasPrice: toBigInt(candidate)
    }
    }
    }
  4. Call sFUEL distribution contract with unique Gas Price
    const response = await randomWallet.sendTransaction(request);
    await provider.waitForTransaction(response.hash, 1);
    await new Promise<void>((resolve) => setTimeout(resolve, 1000));