diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b02e156d..84a960b7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,11 +7,12 @@ env: jobs: check: - strategy: - fail-fast: true - name: Foundry runs-on: ubuntu-latest + + permissions: + contents: write + steps: - uses: actions/checkout@v4 with: @@ -37,3 +38,14 @@ jobs: run: | forge test -vvv id: test + + - name: Run Forge docs + run: | + forge doc --out docs/docs/contracts + python3 docs/scripts/forge_doc_reformat.py + + - name: Commit Forge docs + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "chore: Generate Foundry docs" + file_pattern: "./docs/" diff --git a/.gitignore b/.gitignore index 83b39d9c..fd706a78 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,4 @@ out/ node_modules/* # generated docs -/docs/docs/api/* \ No newline at end of file +/docs/docs/api/* diff --git a/docs/docs/contracts/.gitignore b/docs/docs/contracts/.gitignore new file mode 100644 index 00000000..4e42a1bc --- /dev/null +++ b/docs/docs/contracts/.gitignore @@ -0,0 +1 @@ +book/ \ No newline at end of file diff --git a/docs/docs/contracts/book.css b/docs/docs/contracts/book.css new file mode 100644 index 00000000..b5ce903f --- /dev/null +++ b/docs/docs/contracts/book.css @@ -0,0 +1,13 @@ +table { + margin: 0 auto; + border-collapse: collapse; + width: 100%; +} + +table td:first-child { + width: 15%; +} + +table td:nth-child(2) { + width: 25%; +} \ No newline at end of file diff --git a/docs/docs/contracts/book.toml b/docs/docs/contracts/book.toml new file mode 100644 index 00000000..353bd356 --- /dev/null +++ b/docs/docs/contracts/book.toml @@ -0,0 +1,12 @@ +[book] +src = "src" +title = "" + +[output.html] +no-section-label = true +additional-js = ["solidity.min.js"] +additional-css = ["book.css"] +git-repository-url = "https://github.com/bob-collective/bob" + +[output.html.fold] +enable = true diff --git a/docs/docs/contracts/contract_references.md b/docs/docs/contracts/contract_references.md new file mode 100644 index 00000000..f109f392 --- /dev/null +++ b/docs/docs/contracts/contract_references.md @@ -0,0 +1,7 @@ +# Smart Contracts + +The source code for the smart contracts can be found in the following repository: + +- [Relay Smart Contracts](https://github.com/bob-collective/bob/tree/master/src/relay) +- [Bridge Smart Contracts](https://github.com/bob-collective/bob/tree/master/src/bridge) +- [Swap Smart Contracts](https://github.com/bob-collective/bob/tree/master/src/swap) diff --git a/docs/docs/contracts/solidity.min.js b/docs/docs/contracts/solidity.min.js new file mode 100644 index 00000000..19249329 --- /dev/null +++ b/docs/docs/contracts/solidity.min.js @@ -0,0 +1,74 @@ +hljs.registerLanguage("solidity",(()=>{"use strict";function e(){try{return!0 +}catch(e){return!1}} +var a=/-?(\b0[xX]([a-fA-F0-9]_?)*[a-fA-F0-9]|(\b[1-9](_?\d)*(\.((\d_?)*\d)?)?|\.\d(_?\d)*)([eE][-+]?\d(_?\d)*)?|\b0)(?!\w|\$)/ +;e()&&(a=a.source.replace(/\\b/g,"(?{ +var a=r(e),o=l(e),c=/[A-Za-z_$][A-Za-z_$0-9.]*/,d=e.inherit(e.TITLE_MODE,{ +begin:/[A-Za-z$_][0-9A-Za-z$_]*/,lexemes:c,keywords:n}),u={className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,lexemes:c,keywords:n, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,o,s]},_={ +className:"operator",begin:/:=|->/};return{keywords:n,lexemes:c, +contains:[a,o,i,t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,_,{ +className:"function",lexemes:c,beginKeywords:"function",end:"{",excludeEnd:!0, +contains:[d,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,_]}]}}, +solAposStringMode:r,solQuoteStringMode:l,HEX_APOS_STRING_MODE:i, +HEX_QUOTE_STRING_MODE:t,SOL_NUMBER:s,isNegativeLookbehindAvailable:e} +;const{baseAssembly:c,solAposStringMode:d,solQuoteStringMode:u,HEX_APOS_STRING_MODE:_,HEX_QUOTE_STRING_MODE:m,SOL_NUMBER:b,isNegativeLookbehindAvailable:E}=o +;return e=>{for(var a=d(e),s=u(e),n=[],i=0;i<32;i++)n[i]=i+1 +;var t=n.map((e=>8*e)),r=[];for(i=0;i<=80;i++)r[i]=i +;var l=n.map((e=>"bytes"+e)).join(" ")+" ",o=t.map((e=>"uint"+e)).join(" ")+" ",g=t.map((e=>"int"+e)).join(" ")+" ",M=[].concat.apply([],t.map((e=>r.map((a=>e+"x"+a))))),p={ +keyword:"var bool string int uint "+g+o+"byte bytes "+l+"fixed ufixed "+M.map((e=>"fixed"+e)).join(" ")+" "+M.map((e=>"ufixed"+e)).join(" ")+" enum struct mapping address new delete if else for while continue break return throw emit try catch revert unchecked _ function modifier event constructor fallback receive error virtual override constant immutable anonymous indexed storage memory calldata external public internal payable pure view private returns import from as using pragma contract interface library is abstract type assembly", +literal:"true false wei gwei szabo finney ether seconds minutes hours days weeks years", +built_in:"self this super selfdestruct suicide now msg block tx abi blockhash gasleft assert require Error Panic sha3 sha256 keccak256 ripemd160 ecrecover addmod mulmod log0 log1 log2 log3 log4" +},O={className:"operator",begin:/[+\-!~*\/%<>&^|=]/ +},C=/[A-Za-z_$][A-Za-z_$0-9]*/,N={className:"params",begin:/\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,lexemes:C,keywords:p, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,s,b,"self"]},f={ +begin:/\.\s*/,end:/[^A-Za-z0-9$_\.]/,excludeBegin:!0,excludeEnd:!0,keywords:{ +built_in:"gas value selector address length push pop send transfer call callcode delegatecall staticcall balance code codehash wrap unwrap name creationCode runtimeCode interfaceId min max" +},relevance:2},y=e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/, +lexemes:C,keywords:p}),w={className:"built_in", +begin:(E()?"(? address) supportedErc20Addresses; +``` + + +## Functions +### addErc20 + + +```solidity +function addErc20(address newErc20) public onlyOwner; +``` + +### mint + + +```solidity +function mint() public; +``` + diff --git a/docs/docs/contracts/src/src/faucet/Erc20Minter.sol/interface.Erc20Mintable.md b/docs/docs/contracts/src/src/faucet/Erc20Minter.sol/interface.Erc20Mintable.md new file mode 100644 index 00000000..29d860d7 --- /dev/null +++ b/docs/docs/contracts/src/src/faucet/Erc20Minter.sol/interface.Erc20Mintable.md @@ -0,0 +1,40 @@ +# Erc20Mintable +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/faucet/Erc20Minter.sol) + + +## Functions +### decimals + + +```solidity +function decimals() external returns (uint256); +``` + +### mint + + +```solidity +function mint(uint256 amount) external; +``` + +### transfer + + +```solidity +function transfer(address to, uint256 amount) external returns (bool); +``` + +### approve + + +```solidity +function approve(address spender, uint256 amount) external; +``` + +### balanceOf + + +```solidity +function balanceOf(address account) external view returns (uint256); +``` + diff --git a/docs/docs/contracts/src/src/paymasters/AccountAbstraction/AATokenPaymaster.sol/contract.PimlicoERC20Paymaster.md b/docs/docs/contracts/src/src/paymasters/AccountAbstraction/AATokenPaymaster.sol/contract.PimlicoERC20Paymaster.md new file mode 100644 index 00000000..87c06724 --- /dev/null +++ b/docs/docs/contracts/src/src/paymasters/AccountAbstraction/AATokenPaymaster.sol/contract.PimlicoERC20Paymaster.md @@ -0,0 +1,229 @@ +# PimlicoERC20Paymaster +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/paymasters/AccountAbstraction/AATokenPaymaster.sol) + +**Inherits:** +BasePaymaster + +An ERC-4337 Paymaster contract by Pimlico which is able to sponsor gas fees in exchange for ERC20 tokens. +The contract refunds excess tokens if the actual gas cost is lower than the initially provided amount. +It also allows updating price configuration and withdrawing tokens by the contract owner. +The contract uses an Oracle to fetch the latest token prices. + +*Inherits from BasePaymaster.* + + +## State Variables +### priceDenominator + +```solidity +uint256 public constant priceDenominator = 1e6; +``` + + +### REFUND_POSTOP_COST + +```solidity +uint256 public constant REFUND_POSTOP_COST = 40000; +``` + + +### token + +```solidity +IERC20 public immutable token; +``` + + +### tokenDecimals + +```solidity +uint256 public immutable tokenDecimals; +``` + + +### tokenOracle + +```solidity +IOracle public immutable tokenOracle; +``` + + +### nativeAssetOracle + +```solidity +IOracle public immutable nativeAssetOracle; +``` + + +### previousPrice + +```solidity +uint192 public previousPrice; +``` + + +### priceMarkup + +```solidity +uint32 public priceMarkup; +``` + + +### priceUpdateThreshold + +```solidity +uint32 public priceUpdateThreshold; +``` + + +## Functions +### constructor + +Initializes the PimlicoERC20Paymaster contract with the given parameters. + + +```solidity +constructor( + IERC20 _token, + IEntryPoint _entryPoint, + IOracle _tokenOracle, + IOracle _nativeAssetOracle, + address _owner, + uint8 _tokenDecimals +) BasePaymaster(_entryPoint); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`_token`|`IERC20`|The ERC20 token used for transaction fee payments.| +|`_entryPoint`|`IEntryPoint`|The EntryPoint contract used in the Account Abstraction infrastructure.| +|`_tokenOracle`|`IOracle`|The Oracle contract used to fetch the latest token prices.| +|`_nativeAssetOracle`|`IOracle`|The Oracle contract used to fetch the latest native asset (ETH, Matic, Avax, etc.) prices.| +|`_owner`|`address`|The address that will be set as the owner of the contract.| +|`_tokenDecimals`|`uint8`|| + + +### updateConfig + +Updates the price markup and price update threshold configurations. + + +```solidity +function updateConfig(uint32 _priceMarkup, uint32 _updateThreshold) external onlyOwner; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`_priceMarkup`|`uint32`|The new price markup percentage (1e6 = 100%).| +|`_updateThreshold`|`uint32`|The new price update threshold percentage (1e6 = 100%).| + + +### withdrawToken + +Allows the contract owner to withdraw a specified amount of tokens from the contract. + + +```solidity +function withdrawToken(address to, uint256 amount) external onlyOwner; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`to`|`address`|The address to transfer the tokens to.| +|`amount`|`uint256`|The amount of tokens to transfer.| + + +### updatePrice + +Updates the token price by fetching the latest price from the Oracle. + + +```solidity +function updatePrice() external; +``` + +### _validatePaymasterUserOp + +Validates a paymaster user operation and calculates the required token amount for the transaction. + + +```solidity +function _validatePaymasterUserOp(UserOperation calldata userOp, bytes32, uint256 requiredPreFund) + internal + override + returns (bytes memory context, uint256 validationResult); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`userOp`|`UserOperation`|The user operation data.| +|``|`bytes32`|| +|`requiredPreFund`|`uint256`|The amount of tokens required for pre-funding.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`context`|`bytes`|The context containing the token amount and user sender address (if applicable).| +|`validationResult`|`uint256`|A uint256 value indicating the result of the validation (always 0 in this implementation).| + + +### _postOp + +Performs post-operation tasks, such as updating the token price and refunding excess tokens. + +*This function is called after a user operation has been executed or reverted.* + + +```solidity +function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal override; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`mode`|`PostOpMode`|The post-operation mode (either successful or reverted).| +|`context`|`bytes`|The context containing the token amount and user sender address.| +|`actualGasCost`|`uint256`|The actual gas cost of the transaction.| + + +### fetchPrice + +Fetches the latest price from the given Oracle. + +*This function is used to get the latest price from the tokenOracle or nativeAssetOracle.* + + +```solidity +function fetchPrice(IOracle _oracle) internal view returns (uint192 price); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`_oracle`|`IOracle`|The Oracle contract to fetch the price from.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`price`|`uint192`|The latest price fetched from the Oracle.| + + +## Events +### ConfigUpdated + +```solidity +event ConfigUpdated(uint32 priceMarkup, uint32 updateThreshold); +``` + +### UserOperationSponsored + +```solidity +event UserOperationSponsored(address indexed user, uint256 actualTokenNeeded, uint256 actualGasCost); +``` + diff --git a/docs/docs/contracts/src/src/paymasters/AccountAbstraction/SafeTransferLib.sol/library.SafeTransferLib.md b/docs/docs/contracts/src/src/paymasters/AccountAbstraction/SafeTransferLib.sol/library.SafeTransferLib.md new file mode 100644 index 00000000..f3432714 --- /dev/null +++ b/docs/docs/contracts/src/src/paymasters/AccountAbstraction/SafeTransferLib.sol/library.SafeTransferLib.md @@ -0,0 +1,131 @@ +# SafeTransferLib +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/paymasters/AccountAbstraction/SafeTransferLib.sol) + +**Authors:** +Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol), Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) + +Safe ETH and ERC20 transfer library that gracefully handles missing return values. + +*Caution! This library won't check that a token has code, responsibility is delegated to the caller.* + + +## State Variables +### _GAS_STIPEND_NO_STORAGE_WRITES +*Suggested gas stipend for contract receiving ETH +that disallows any storage writes.* + + +```solidity +uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300; +``` + + +### _GAS_STIPEND_NO_GRIEF +*Suggested gas stipend for contract receiving ETH to perform a few +storage reads and writes, but low enough to prevent griefing. +Multiply by a small constant (e.g. 2), if needed.* + + +```solidity +uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000; +``` + + +## Functions +### safeTransferFrom + +*Sends `amount` of ERC20 `token` from `from` to `to`. +Reverts upon failure. +The `from` account must have at least `amount` approved for +the current contract to manage.* + + +```solidity +function safeTransferFrom(address token, address from, address to, uint256 amount) internal; +``` + +### safeTransferAllFrom + +*Sends all of ERC20 `token` from `from` to `to`. +Reverts upon failure. +The `from` account must have at least `amount` approved for +the current contract to manage.* + + +```solidity +function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount); +``` + +### safeTransfer + +*Sends `amount` of ERC20 `token` from the current contract to `to`. +Reverts upon failure.* + + +```solidity +function safeTransfer(address token, address to, uint256 amount) internal; +``` + +### safeTransferAll + +*Sends all of ERC20 `token` from the current contract to `to`. +Reverts upon failure.* + + +```solidity +function safeTransferAll(address token, address to) internal returns (uint256 amount); +``` + +### safeApprove + +*Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. +Reverts upon failure.* + + +```solidity +function safeApprove(address token, address to, uint256 amount) internal; +``` + +### balanceOf + +*Returns the amount of ERC20 `token` owned by `account`. +Returns zero if the `token` does not exist.* + + +```solidity +function balanceOf(address token, address account) internal view returns (uint256 amount); +``` + +## Errors +### ETHTransferFailed +*The ETH transfer has failed.* + + +```solidity +error ETHTransferFailed(); +``` + +### TransferFromFailed +*The ERC20 `transferFrom` has failed.* + + +```solidity +error TransferFromFailed(); +``` + +### TransferFailed +*The ERC20 `transfer` has failed.* + + +```solidity +error TransferFailed(); +``` + +### ApproveFailed +*The ERC20 `approve` has failed.* + + +```solidity +error ApproveFailed(); +``` + diff --git a/docs/docs/contracts/src/src/paymasters/OnboardingPaymaster.sol/contract.OnboardingPaymaster.md b/docs/docs/contracts/src/src/paymasters/OnboardingPaymaster.sol/contract.OnboardingPaymaster.md new file mode 100644 index 00000000..2c3b0049 --- /dev/null +++ b/docs/docs/contracts/src/src/paymasters/OnboardingPaymaster.sol/contract.OnboardingPaymaster.md @@ -0,0 +1,102 @@ +# OnboardingPaymaster +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/paymasters/OnboardingPaymaster.sol) + +**Inherits:** +BasePaymaster + + +## State Variables +### whitelistedContract + +```solidity +address public whitelistedContract; +``` + + +### whitelistedSelector + +```solidity +uint32 public whitelistedSelector; +``` + + +### gasUsedByPost + +```solidity +uint256 public gasUsedByPost; +``` + + +## Functions +### constructor + + +```solidity +constructor(address _whitelistedContract, uint32 _whitelistedSelector); +``` + +### versionPaymaster + + +```solidity +function versionPaymaster() external view virtual override returns (string memory); +``` + +### setPostGasUsage + + +```solidity +function setPostGasUsage(uint256 _gasUsedByPost) external onlyOwner; +``` + +### _getPaymasterData + + +```solidity +function _getPaymasterData(bytes memory paymasterData) private returns (IERC20 token, uint256 maxTokens); +``` + +### getSelector + + +```solidity +function getSelector(bytes calldata call) public view returns (uint32); +``` + +### _preRelayedCall + + +```solidity +function _preRelayedCall( + GsnTypes.RelayRequest calldata relayRequest, + bytes calldata signature, + bytes calldata approvalData, + uint256 maxPossibleGas +) internal virtual override returns (bytes memory context, bool revertOnRecipientRevert); +``` + +### _postRelayedCall + + +```solidity +function _postRelayedCall( + bytes calldata context, + bool, + uint256 gasUseWithoutPost, + GsnTypes.RelayData calldata relayData +) internal virtual override; +``` + +## Events +### PreRelay + +```solidity +event PreRelay(address indexed sender); +``` + +### PostRelay + +```solidity +event PostRelay(address indexed sender); +``` + diff --git a/docs/docs/contracts/src/src/paymasters/Oracle.sol/contract.DummyOracle.md b/docs/docs/contracts/src/src/paymasters/Oracle.sol/contract.DummyOracle.md new file mode 100644 index 00000000..8ab44a9a --- /dev/null +++ b/docs/docs/contracts/src/src/paymasters/Oracle.sol/contract.DummyOracle.md @@ -0,0 +1,40 @@ +# DummyOracle +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/paymasters/Oracle.sol) + +**Inherits:** +[IOracle](../../paymasters/Oracle.sol/interface.IOracle.md) + + +## State Variables +### price + +```solidity +int256 public price; +``` + + +## Functions +### decimals + + +```solidity +function decimals() external view returns (uint8); +``` + +### latestRoundData + + +```solidity +function latestRoundData() + external + view + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); +``` + +### setPrice + + +```solidity +function setPrice(int256 _price) public; +``` + diff --git a/docs/docs/contracts/src/src/paymasters/Oracle.sol/interface.IOracle.md b/docs/docs/contracts/src/src/paymasters/Oracle.sol/interface.IOracle.md new file mode 100644 index 00000000..ae9d6bb6 --- /dev/null +++ b/docs/docs/contracts/src/src/paymasters/Oracle.sol/interface.IOracle.md @@ -0,0 +1,22 @@ +# IOracle +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/paymasters/Oracle.sol) + + +## Functions +### decimals + + +```solidity +function decimals() external view returns (uint8); +``` + +### latestRoundData + + +```solidity +function latestRoundData() + external + view + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); +``` + diff --git a/docs/docs/contracts/src/src/paymasters/OracleTokenPaymaster.sol/contract.OracleTokenPaymaster.md b/docs/docs/contracts/src/src/paymasters/OracleTokenPaymaster.sol/contract.OracleTokenPaymaster.md new file mode 100644 index 00000000..acd33cf5 --- /dev/null +++ b/docs/docs/contracts/src/src/paymasters/OracleTokenPaymaster.sol/contract.OracleTokenPaymaster.md @@ -0,0 +1,191 @@ +# OracleTokenPaymaster +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/paymasters/OracleTokenPaymaster.sol) + +**Inherits:** +BasePaymaster + +A very basic paymaster that makes the payer pay in ERC20 tokens. +- The token prices need to be provided by an IOracle. +- No swaps are done - the paymaster simply receives ERC20 tokens. This means +that over time, the paymaster's eth balance will decrease. It is up to the +owner of the contract to replenish the eth balance. +- The owner of the contract can withdraw their received erc20 balances. +- Users specify an erc20 address and a maximum amount they are willing to pay +for the tx. This reduces the trust put in the oracle. + + +## State Variables +### nativeTokenOracle + +```solidity +IOracle nativeTokenOracle; +``` + + +### tokenOracles + +```solidity +mapping(IERC20 => TokenDetails) public tokenOracles; +``` + + +### gasUsedByPost + +```solidity +uint256 public gasUsedByPost; +``` + + +## Functions +### constructor + + +```solidity +constructor(IOracle _nativeTokenOracle); +``` + +### versionPaymaster + + +```solidity +function versionPaymaster() external view virtual override returns (string memory); +``` + +### addOracle + + +```solidity +function addOracle(IERC20 _token, uint256 _decimals, IOracle _oracle) external onlyOwner; +``` + +### fetchPrice + + +```solidity +function fetchPrice(IOracle _oracle) internal view returns (uint192 price); +``` + +### _ethToTokens + + +```solidity +function _ethToTokens(IERC20 token, uint256 ethAmount) internal view returns (uint256); +``` + +### setPostGasUsage + + +```solidity +function setPostGasUsage(uint256 _gasUsedByPost) external onlyOwner; +``` + +### withdrawAll + + +```solidity +function withdrawAll(IERC20 token) external onlyOwner; +``` + +### getPayer + + +```solidity +function getPayer(GsnTypes.RelayRequest calldata relayRequest) public view virtual returns (address); +``` + +### _getPaymasterData + + +```solidity +function _getPaymasterData(bytes memory paymasterData) private returns (IERC20 token, uint256 maxTokens); +``` + +### _calculatePreCharge + + +```solidity +function _calculatePreCharge(IERC20 token, GsnTypes.RelayRequest calldata relayRequest, uint256 maxPossibleGas) + internal + returns (address payer, uint256 ethPrecharge, uint256 tokenPreCharge); +``` + +### _verifyPaymasterData + + +```solidity +function _verifyPaymasterData(GsnTypes.RelayRequest calldata relayRequest) internal view virtual override; +``` + +### __preRelayedCall + + +```solidity +function __preRelayedCall( + GsnTypes.RelayRequest calldata relayRequest, + bytes calldata signature, + bytes calldata approvalData, + uint256 maxPossibleGas +) public returns (bytes memory context, bool revertOnRecipientRevert); +``` + +### _preRelayedCall + + +```solidity +function _preRelayedCall( + GsnTypes.RelayRequest calldata relayRequest, + bytes calldata signature, + bytes calldata approvalData, + uint256 maxPossibleGas +) internal virtual override returns (bytes memory context, bool revertOnRecipientRevert); +``` + +### _postRelayedCall + + +```solidity +function _postRelayedCall( + bytes calldata context, + bool, + uint256 gasUseWithoutPost, + GsnTypes.RelayData calldata relayData +) internal virtual override; +``` + +### _postRelayedCallInternal + + +```solidity +function _postRelayedCallInternal( + address payer, + uint256 tokenPrecharge, + uint256 valueRequested, + uint256 gasUseWithoutPost, + GsnTypes.RelayData calldata relayData, + IERC20 token +) internal; +``` + +## Events +### PreRelayPayment + +```solidity +event PreRelayPayment(uint256 ethAmount, IERC20 token, uint256 tokenAmount, address indexed payer); +``` + +### PostRelay + +```solidity +event PostRelay(uint256 actualEthAmount, IERC20 token, uint256 actualTokenAmount, address payer); +``` + +## Structs +### TokenDetails + +```solidity +struct TokenDetails { + uint256 div; + IOracle oracle; +} +``` + diff --git a/docs/docs/contracts/src/src/relay/LightRelay.sol/contract.LightRelay.md b/docs/docs/contracts/src/src/relay/LightRelay.sol/contract.LightRelay.md new file mode 100644 index 00000000..88376108 --- /dev/null +++ b/docs/docs/contracts/src/src/relay/LightRelay.sol/contract.LightRelay.md @@ -0,0 +1,391 @@ +# LightRelay +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/relay/LightRelay.sol) + +**Inherits:** +Ownable, [ILightRelay](../../relay/LightRelay.sol/interface.ILightRelay.md) + +*THE RELAY MUST NOT BE USED BEFORE GENESIS AND AT LEAST ONE RETARGET.* + + +## State Variables +### ready + +```solidity +bool public ready; +``` + + +### authorizationRequired + +```solidity +bool public authorizationRequired; +``` + + +### proofLength + +```solidity +uint64 public proofLength; +``` + + +### genesisEpoch + +```solidity +uint64 public genesisEpoch; +``` + + +### currentEpoch + +```solidity +uint64 public currentEpoch; +``` + + +### currentEpochDifficulty + +```solidity +uint256 internal currentEpochDifficulty; +``` + + +### prevEpochDifficulty + +```solidity +uint256 internal prevEpochDifficulty; +``` + + +### epochs + +```solidity +mapping(uint256 => Epoch) internal epochs; +``` + + +### isAuthorized + +```solidity +mapping(address => bool) public isAuthorized; +``` + + +## Functions +### relayActive + + +```solidity +modifier relayActive(); +``` + +### genesis + +Establish a starting point for the relay by providing the +target, timestamp and blockheight of the first block of the relay +genesis epoch. + +*If the relay is used by querying the current and previous epoch +difficulty, at least one retarget needs to be provided after genesis; +otherwise the prevEpochDifficulty will be uninitialised and zero.* + + +```solidity +function genesis(bytes calldata genesisHeader, uint256 genesisHeight, uint64 genesisProofLength) external onlyOwner; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`genesisHeader`|`bytes`|The first block header of the genesis epoch.| +|`genesisHeight`|`uint256`|The block number of the first block of the epoch.| +|`genesisProofLength`|`uint64`|The number of blocks required to accept a proof.| + + +### setProofLength + +Set the number of blocks required to accept a header chain. + +*For production, a high number (e.g. 20-50) is recommended. +Small numbers are accepted but should only be used for testing.* + + +```solidity +function setProofLength(uint64 newLength) external relayActive onlyOwner; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`newLength`|`uint64`|The required number of blocks. Must be less than 2016.| + + +### setAuthorizationStatus + +Set whether the relay requires retarget submitters to be +pre-authorised by governance. + + +```solidity +function setAuthorizationStatus(bool status) external onlyOwner; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`status`|`bool`|True if authorisation is to be required, false if not.| + + +### authorize + +Authorise the given address to submit retarget proofs. + + +```solidity +function authorize(address submitter) external onlyOwner; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`submitter`|`address`|The address to be authorised.| + + +### deauthorize + +Rescind the authorisation of the submitter to retarget. + + +```solidity +function deauthorize(address submitter) external onlyOwner; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`submitter`|`address`|The address to be deauthorised.| + + +### retarget + +Add a new epoch to the relay by providing a proof +of the difficulty before and after the retarget. + +*Checks that the first X blocks are valid in the most recent epoch, +that the difficulty of the new epoch is calculated correctly according +to the block timestamps, and that the next X blocks would be valid in +the new epoch. +We have no information of block heights, so we cannot enforce that +retargets only happen every 2016 blocks; instead, we assume that this +is the case if a valid proof of work is provided. +It is possible to cheat the relay by providing X blocks from earlier in +the most recent epoch, and then mining X new blocks after them. +However, each of these malicious blocks would have to be mined to a +higher difficulty than the legitimate ones. +Alternatively, if the retarget has not been performed yet, one could +first mine X blocks in the old difficulty with timestamps set far in +the future, and then another X blocks at a greatly reduced difficulty. +In either case, cheating the relay requires more work than mining X +legitimate blocks. +Only the most recent epoch is vulnerable to these attacks; once a +retarget has been proven to the relay, the epoch is immutable even if a +contradictory proof were to be presented later.* + + +```solidity +function retarget(bytes memory headers) external relayActive; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`headers`|`bytes`|A chain of headers including the last X blocks before the retarget, followed by the first X blocks after the retarget, where X equals the current proof length.| + + +### validateChain + +Check whether a given chain of headers should be accepted as +valid within the rules of the relay. +If the validation fails, this function throws an exception. + +*A chain of headers is accepted as valid if: +- Its length is between 2 and 2015 headers. +- Headers in the chain are sequential and refer to previous digests. +- Each header is mined with the correct amount of work. +- The difficulty in each header matches an epoch of the relay, +as determined by the headers' timestamps. The headers must be between +the genesis epoch and the latest proven epoch (inclusive). +If the chain contains a retarget, it is accepted if the retarget has +already been proven to the relay. +If the chain contains blocks of an epoch that has not been proven to +the relay (after a retarget within the header chain, or when the entire +chain falls within an epoch that has not been proven yet), it will be +rejected. +One exception to this is when two subsequent epochs have exactly the +same difficulty; headers from the latter epoch will be accepted if the +previous epoch has been proven to the relay. +This is because it is not possible to distinguish such headers from +headers of the previous epoch. +If the difficulty increases significantly between relay genesis and the +present, creating fraudulent proofs for earlier epochs becomes easier. +Users of the relay should check the timestamps of valid headers and +only accept appropriately recent ones.* + + +```solidity +function validateChain(bytes memory headers) + external + view + returns (uint256 startingHeaderTimestamp, uint256 headerCount); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`headers`|`bytes`|A chain of 2 to 2015 bitcoin headers.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`startingHeaderTimestamp`|`uint256`|The timestamp of the first header.| +|`headerCount`|`uint256`|The number of headers.| + + +### getBlockDifficulty + +Get the difficulty of the specified block. + + +```solidity +function getBlockDifficulty(uint256 blockNumber) external view returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`blockNumber`|`uint256`|The number of the block. Must fall within the relay range (at or after the relay genesis, and at or before the end of the most recent epoch proven to the relay).| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The difficulty of the epoch.| + + +### getRelayRange + +Get the range of blocks the relay can accept proofs for. + +*Assumes that the genesis has been set correctly. +Additionally, if the next epoch after the current one has the exact +same difficulty, headers for it can be validated as well. +This function should be used for informative purposes, +e.g. to determine whether a retarget must be provided before submitting +a header chain for validation.* + + +```solidity +function getRelayRange() external view returns (uint256 relayGenesis, uint256 currentEpochEnd); +``` +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`relayGenesis`|`uint256`|The height of the earliest block that can be included in header chains for the relay to validate.| +|`currentEpochEnd`|`uint256`|The height of the last block that can be included in header chains for the relay to validate.| + + +### getCurrentEpochDifficulty + +Returns the difficulty of the current epoch. + +*returns 0 if the relay is not ready.* + + +```solidity +function getCurrentEpochDifficulty() external view virtual returns (uint256); +``` +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The difficulty of the current epoch.| + + +### getPrevEpochDifficulty + +Returns the difficulty of the previous epoch. + +*Returns 0 if the relay is not ready or has not had a retarget.* + + +```solidity +function getPrevEpochDifficulty() external view virtual returns (uint256); +``` +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The difficulty of the previous epoch.| + + +### getCurrentAndPrevEpochDifficulty + + +```solidity +function getCurrentAndPrevEpochDifficulty() external view returns (uint256 current, uint256 previous); +``` + +### getEpochDifficulty + +Get the difficulty of the specified epoch. + + +```solidity +function getEpochDifficulty(uint256 epochNumber) public view returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`epochNumber`|`uint256`|The number of the epoch (the height of the first block of the epoch, divided by 2016). Must fall within the relay range.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The difficulty of the epoch.| + + +### validateHeader + +Check that the specified header forms a correct chain with the +digest of the previous header (if provided), and has sufficient work. + +*Throws an exception if the header's chain or PoW are invalid. +Performs no other validation.* + + +```solidity +function validateHeader(bytes memory headers, uint256 start, bytes32 prevDigest) + internal + view + returns (bytes32 digest, uint256 target); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`headers`|`bytes`|The byte array containing the header of interest.| +|`start`|`uint256`|The start of the header in the array.| +|`prevDigest`|`bytes32`|The digest of the previous header (optional; providing zeros for the digest skips the check).| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`digest`|`bytes32`|The digest of the current header.| +|`target`|`uint256`|The PoW target of the header.| + + diff --git a/docs/docs/contracts/src/src/relay/LightRelay.sol/interface.ILightRelay.md b/docs/docs/contracts/src/src/relay/LightRelay.sol/interface.ILightRelay.md new file mode 100644 index 00000000..4f3d544c --- /dev/null +++ b/docs/docs/contracts/src/src/relay/LightRelay.sol/interface.ILightRelay.md @@ -0,0 +1,83 @@ +# ILightRelay +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/relay/LightRelay.sol) + +**Inherits:** +[IRelay](../../bridge/IRelay.sol/interface.IRelay.md) + + +## Functions +### retarget + + +```solidity +function retarget(bytes memory headers) external; +``` + +### validateChain + + +```solidity +function validateChain(bytes memory headers) + external + view + returns (uint256 startingHeaderTimestamp, uint256 headerCount); +``` + +### getBlockDifficulty + + +```solidity +function getBlockDifficulty(uint256 blockNumber) external view returns (uint256); +``` + +### getEpochDifficulty + + +```solidity +function getEpochDifficulty(uint256 epochNumber) external view returns (uint256); +``` + +### getRelayRange + + +```solidity +function getRelayRange() external view returns (uint256 relayGenesis, uint256 currentEpochEnd); +``` + +## Events +### Genesis + +```solidity +event Genesis(uint256 blockHeight); +``` + +### Retarget + +```solidity +event Retarget(uint256 oldDifficulty, uint256 newDifficulty); +``` + +### ProofLengthChanged + +```solidity +event ProofLengthChanged(uint256 newLength); +``` + +### AuthorizationRequirementChanged + +```solidity +event AuthorizationRequirementChanged(bool newStatus); +``` + +### SubmitterAuthorized + +```solidity +event SubmitterAuthorized(address submitter); +``` + +### SubmitterDeauthorized + +```solidity +event SubmitterDeauthorized(address submitter); +``` + diff --git a/docs/docs/contracts/src/src/relay/LightRelay.sol/library.RelayUtils.md b/docs/docs/contracts/src/src/relay/LightRelay.sol/library.RelayUtils.md new file mode 100644 index 00000000..7886e4ec --- /dev/null +++ b/docs/docs/contracts/src/src/relay/LightRelay.sol/library.RelayUtils.md @@ -0,0 +1,30 @@ +# RelayUtils +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/relay/LightRelay.sol) + + +## Functions +### extractTimestampAt + +Extract the timestamp of the header at the given position. + +*Assumes that the specified position contains a valid header. +Performs no validation whatsoever.* + + +```solidity +function extractTimestampAt(bytes memory headers, uint256 at) internal pure returns (uint32); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`headers`|`bytes`|Byte array containing the header of interest.| +|`at`|`uint256`|The start of the header in the array.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint32`|The timestamp of the header.| + + diff --git a/docs/docs/contracts/src/src/relay/LightRelay.sol/struct.Epoch.md b/docs/docs/contracts/src/src/relay/LightRelay.sol/struct.Epoch.md new file mode 100644 index 00000000..7d27b34a --- /dev/null +++ b/docs/docs/contracts/src/src/relay/LightRelay.sol/struct.Epoch.md @@ -0,0 +1,11 @@ +# Epoch +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/relay/LightRelay.sol) + + +```solidity +struct Epoch { + uint32 timestamp; + uint224 target; +} +``` + diff --git a/docs/docs/contracts/src/src/relay/TestLightRelay.sol/contract.TestLightRelay.md b/docs/docs/contracts/src/src/relay/TestLightRelay.sol/contract.TestLightRelay.md new file mode 100644 index 00000000..6d5000d7 --- /dev/null +++ b/docs/docs/contracts/src/src/relay/TestLightRelay.sol/contract.TestLightRelay.md @@ -0,0 +1,28 @@ +# TestLightRelay +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/relay/TestLightRelay.sol) + +**Inherits:** +[LightRelay](../../relay/LightRelay.sol/contract.LightRelay.md) + +TestLightRelay is a stub version of LightRelay intended to be +used on for testing network. It allows to set the relay's +difficulty based on arbitrary Bitcoin headers thus effectively +bypass the validation of difficulties of Bitcoin testnet blocks. +Since difficulty in Bitcoin testnet often falls to `1` it would not +be possible to validate blocks with the real LightRelay. + +*Notice that TestLightRelay is derived from LightRelay so that the two +contracts have the same API and correct bindings can be generated.* + + +## Functions +### setDifficultyFromHeaders + +Sets the current and previous difficulty based on the difficulty +inferred from the provided Bitcoin headers. + + +```solidity +function setDifficultyFromHeaders(bytes memory bitcoinHeaders) external; +``` + diff --git a/docs/docs/contracts/src/src/swap/Bridge.sol/contract.Bridge.md b/docs/docs/contracts/src/src/swap/Bridge.sol/contract.Bridge.md new file mode 100644 index 00000000..26e1911e --- /dev/null +++ b/docs/docs/contracts/src/src/swap/Bridge.sol/contract.Bridge.md @@ -0,0 +1,162 @@ +# Bridge +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/swap/Bridge.sol) + +**Inherits:** +ERC2771Recipient + + +## State Variables +### number + +```solidity +uint256 public number; +``` + + +### collateralThreshold + +```solidity +uint256 public collateralThreshold; +``` + + +### nextOrderId + +```solidity +uint256 nextOrderId; +``` + + +### orders + +```solidity +mapping(uint256 => Order) public orders; +``` + + +### suppliedCollateral + +```solidity +mapping(address => uint256) public suppliedCollateral; +``` + + +### totalCollateral + +```solidity +uint256 totalCollateral; +``` + + +### wrapped + +```solidity +BobWrappedBtc wrapped = new BobWrappedBtc(); +``` + + +## Functions +### constructor + + +```solidity +constructor(uint256 threshold); +``` + +### mint + +lock COL in exchange for zBTC and cCOL + + +```solidity +function mint() public payable; +``` + +### requestSwap + +request zBTC to be redeemed for given amount of BTC. + + +```solidity +function requestSwap(uint256 amountZbtc, uint256 amountBtc, BitcoinAddress calldata bitcoinAddress) public; +``` + +### acceptSwap + + +```solidity +function acceptSwap(uint256 id) public; +``` + +### cancelSwap + + +```solidity +function cancelSwap(uint256 id) public; +``` + +### executeSwap + + +```solidity +function executeSwap(uint256 id, TransactionProof calldata transactionProof) public; +``` + +### liquidate + + +```solidity +function liquidate(uint256 amountZbtc) public; +``` + +### withdraw + + +```solidity +function withdraw() public; +``` + +### colToBtc + + +```solidity +function colToBtc(uint256 collateral) internal pure returns (uint256); +``` + +### btcToCol + + +```solidity +function btcToCol(uint256 collateral) internal pure returns (uint256); +``` + +## Structs +### Order + +```solidity +struct Order { + bool open; + uint256 amountZbtc; + uint256 amountBtc; + address requesterAddress; + address accepterAddress; + BitcoinAddress bitcoinAddress; +} +``` + +### BitcoinAddress + +```solidity +struct BitcoinAddress { + bytes scriptPubKey; +} +``` + +### TransactionProof + +```solidity +struct TransactionProof { + uint256 dummy; +} +``` + diff --git a/docs/docs/contracts/src/src/swap/Btc_Marketplace.sol/contract.BtcMarketPlace.md b/docs/docs/contracts/src/src/swap/Btc_Marketplace.sol/contract.BtcMarketPlace.md new file mode 100644 index 00000000..aaa7b711 --- /dev/null +++ b/docs/docs/contracts/src/src/swap/Btc_Marketplace.sol/contract.BtcMarketPlace.md @@ -0,0 +1,345 @@ +# BtcMarketPlace +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/swap/Btc_Marketplace.sol) + +**Inherits:** +ERC2771Recipient + + +## State Variables +### btcBuyOrders + +```solidity +mapping(uint256 => BtcBuyOrder) public btcBuyOrders; +``` + + +### acceptedBtcBuyOrders + +```solidity +mapping(uint256 => AcceptedBtcBuyOrder) public acceptedBtcBuyOrders; +``` + + +### btcSellOrders + +```solidity +mapping(uint256 => BtcSellOrder) public btcSellOrders; +``` + + +### acceptedBtcSellOrders + +```solidity +mapping(uint256 => AcceptedBtcSellOrder) public acceptedBtcSellOrders; +``` + + +### nextOrderId + +```solidity +uint256 nextOrderId; +``` + + +### REQUEST_EXPIRATION_SECONDS + +```solidity +uint256 public constant REQUEST_EXPIRATION_SECONDS = 6 hours; +``` + + +### relay + +```solidity +BridgeState.Storage internal relay; +``` + + +### testLightRelay + +```solidity +TestLightRelay internal testLightRelay; +``` + + +## Functions +### constructor + + +```solidity +constructor(IRelay _relay, address erc2771Forwarder); +``` + +### setRelay + + +```solidity +function setRelay(IRelay _relay) internal; +``` + +### placeBtcSellOrder + + +```solidity +function placeBtcSellOrder(uint256 amountBtc, address buyingToken, uint256 buyAmount) public; +``` + +### acceptBtcSellOrder + + +```solidity +function acceptBtcSellOrder(uint256 id, BitcoinAddress calldata bitcoinAddress, uint256 amountBtc) + public + returns (uint256); +``` + +### proofBtcSellOrder + + +```solidity +function proofBtcSellOrder(uint256 id, BitcoinTx.Info calldata transaction, BitcoinTx.Proof calldata proof) public; +``` + +### withdrawBtcSellOrder + + +```solidity +function withdrawBtcSellOrder(uint256 id) public; +``` + +### cancelAcceptedBtcSellOrder + + +```solidity +function cancelAcceptedBtcSellOrder(uint256 id) public; +``` + +### placeBtcBuyOrder + + +```solidity +function placeBtcBuyOrder( + uint256 amountBtc, + BitcoinAddress calldata bitcoinAddress, + address sellingToken, + uint256 saleAmount +) public; +``` + +### acceptBtcBuyOrder + + +```solidity +function acceptBtcBuyOrder(uint256 id, uint256 amountBtc) public returns (uint256); +``` + +### proofBtcBuyOrder + + +```solidity +function proofBtcBuyOrder(uint256 id, BitcoinTx.Info calldata transaction, BitcoinTx.Proof calldata proof) public; +``` + +### withdrawBtcBuyOrder + + +```solidity +function withdrawBtcBuyOrder(uint256 id) public; +``` + +### cancelAcceptedBtcBuyOrder + + +```solidity +function cancelAcceptedBtcBuyOrder(uint256 id) public; +``` + +### getOpenBtcSellOrders + + +```solidity +function getOpenBtcSellOrders() external view returns (BtcSellOrder[] memory, uint256[] memory); +``` + +### getOpenAcceptedBtcSellOrders + + +```solidity +function getOpenAcceptedBtcSellOrders() external view returns (AcceptedBtcSellOrder[] memory, uint256[] memory); +``` + +### getOpenBtcBuyOrders + + +```solidity +function getOpenBtcBuyOrders() external view returns (BtcBuyOrder[] memory, uint256[] memory); +``` + +### getOpenAcceptedBtcBuyOrders + + +```solidity +function getOpenAcceptedBtcBuyOrders() external view returns (AcceptedBtcBuyOrder[] memory, uint256[] memory); +``` + +### _checkBitcoinTxOutput + +Checks output script pubkey (recipient address) and amount. +Reverts if transaction amount is lower or bitcoin address is not found. + + +```solidity +function _checkBitcoinTxOutput( + uint256 expectedBtcAmount, + BitcoinAddress storage bitcoinAddress, + BitcoinTx.Info calldata transaction +) private; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`expectedBtcAmount`|`uint256`|BTC amount requested in order.| +|`bitcoinAddress`|`BitcoinAddress`|Recipient's bitcoin address.| +|`transaction`|`BitcoinTx.Info`|Transaction fulfilling the order.| + + +## Events +### placeBtcSellOrderEvent + +```solidity +event placeBtcSellOrderEvent(uint256 indexed orderId, uint256 amountBtc, address buyingToken, uint256 buyAmount); +``` + +### acceptBtcSellOrderEvent + +```solidity +event acceptBtcSellOrderEvent( + uint256 indexed id, + uint256 indexed acceptId, + BitcoinAddress bitcoinAddress, + uint256 amountBtc, + uint256 ercAmount, + address ercToken +); +``` + +### proofBtcSellOrderEvent + +```solidity +event proofBtcSellOrderEvent(uint256 id); +``` + +### withdrawBtcSellOrderEvent + +```solidity +event withdrawBtcSellOrderEvent(uint256 id); +``` + +### cancelAcceptedBtcSellOrderEvent + +```solidity +event cancelAcceptedBtcSellOrderEvent(uint256 id); +``` + +### placeBtcBuyOrderEvent + +```solidity +event placeBtcBuyOrderEvent(uint256 amountBtc, BitcoinAddress bitcoinAddress, address sellingToken, uint256 saleAmount); +``` + +### acceptBtcBuyOrderEvent + +```solidity +event acceptBtcBuyOrderEvent( + uint256 indexed orderId, uint256 indexed acceptId, uint256 amountBtc, uint256 ercAmount, address ercToken +); +``` + +### proofBtcBuyOrderEvent + +```solidity +event proofBtcBuyOrderEvent(uint256 id); +``` + +### withdrawBtcBuyOrderEvent + +```solidity +event withdrawBtcBuyOrderEvent(uint256 id); +``` + +### cancelAcceptedBtcBuyOrderEvent + +```solidity +event cancelAcceptedBtcBuyOrderEvent(uint256 id); +``` + +## Structs +### BtcSellOrder + +```solidity +struct BtcSellOrder { + uint256 amountBtc; + address askingToken; + uint256 askingAmount; + address requester; +} +``` + +### AcceptedBtcSellOrder + +```solidity +struct AcceptedBtcSellOrder { + uint256 orderId; + BitcoinAddress bitcoinAddress; + uint256 amountBtc; + address ercToken; + uint256 ercAmount; + address requester; + address accepter; + uint256 acceptTime; +} +``` + +### BtcBuyOrder + +```solidity +struct BtcBuyOrder { + uint256 amountBtc; + BitcoinAddress bitcoinAddress; + address offeringToken; + uint256 offeringAmount; + address requester; +} +``` + +### AcceptedBtcBuyOrder + +```solidity +struct AcceptedBtcBuyOrder { + uint256 orderId; + uint256 amountBtc; + address ercToken; + uint256 ercAmount; + address requester; + address accepter; + uint256 acceptTime; +} +``` + +### BitcoinAddress + +```solidity +struct BitcoinAddress { + bytes scriptPubKey; +} +``` + +### TransactionProof + +```solidity +struct TransactionProof { + uint256 dummy; +} +``` + diff --git a/docs/docs/contracts/src/src/swap/Faucet.sol/contract.Faucet.md b/docs/docs/contracts/src/src/swap/Faucet.sol/contract.Faucet.md new file mode 100644 index 00000000..6eacb164 --- /dev/null +++ b/docs/docs/contracts/src/src/swap/Faucet.sol/contract.Faucet.md @@ -0,0 +1,51 @@ +# Faucet +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/swap/Faucet.sol) + +**Inherits:** +Ownable, ERC2771Recipient + + +## State Variables +### nextTokenId + +```solidity +uint256 nextTokenId; +``` + + +### supportedErc20Addresses + +```solidity +mapping(uint256 => address) supportedErc20Addresses; +``` + + +## Functions +### addErc20 + + +```solidity +function addErc20(address newErc20) public onlyOwner; +``` + +### mint + + +```solidity +function mint() public; +``` + +### _msgSender + + +```solidity +function _msgSender() internal view override(Context, ERC2771Recipient) returns (address sender); +``` + +### _msgData + + +```solidity +function _msgData() internal view override(Context, ERC2771Recipient) returns (bytes calldata); +``` + diff --git a/docs/docs/contracts/src/src/swap/Faucet.sol/interface.Erc20Mintable.md b/docs/docs/contracts/src/src/swap/Faucet.sol/interface.Erc20Mintable.md new file mode 100644 index 00000000..aa45b213 --- /dev/null +++ b/docs/docs/contracts/src/src/swap/Faucet.sol/interface.Erc20Mintable.md @@ -0,0 +1,26 @@ +# Erc20Mintable +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/swap/Faucet.sol) + + +## Functions +### decimals + + +```solidity +function decimals() external returns (uint256); +``` + +### mint + + +```solidity +function mint(uint256 amount) external; +``` + +### transfer + + +```solidity +function transfer(address to, uint256 value) external returns (bool); +``` + diff --git a/docs/docs/contracts/src/src/swap/Marketplace.sol/contract.MarketPlace.md b/docs/docs/contracts/src/src/swap/Marketplace.sol/contract.MarketPlace.md new file mode 100644 index 00000000..ee9df890 --- /dev/null +++ b/docs/docs/contracts/src/src/swap/Marketplace.sol/contract.MarketPlace.md @@ -0,0 +1,97 @@ +# MarketPlace +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/swap/Marketplace.sol) + +**Inherits:** +ERC2771Recipient + + +## State Variables +### ercErcOrders + +```solidity +mapping(uint256 => Order) public ercErcOrders; +``` + + +### nextOrderId + +```solidity +uint256 public nextOrderId; +``` + + +## Functions +### constructor + + +```solidity +constructor(address erc2771Forwarder); +``` + +### placeErcErcOrder + + +```solidity +function placeErcErcOrder(address sellingToken, uint256 saleAmount, address buyingToken, uint256 buyAmount) public; +``` + +### acceptErcErcOrder + + +```solidity +function acceptErcErcOrder(uint256 id, uint256 saleAmount) public; +``` + +### withdrawErcErcOrder + + +```solidity +function withdrawErcErcOrder(uint256 id) public; +``` + +### getOpenOrders + + +```solidity +function getOpenOrders() external view returns (Order[] memory, uint256[] memory); +``` + +## Events +### placeOrder + +```solidity +event placeOrder( + uint256 indexed orderId, + address indexed requesterAddress, + uint256 offeringAmount, + address offeringToken, + uint256 askingAmount, + address askingToken +); +``` + +### acceptOrder + +```solidity +event acceptOrder(uint256 indexed orderId, address indexed who, uint256 buyAmount, uint256 saleAmount); +``` + +### withdrawOrder + +```solidity +event withdrawOrder(uint256 indexed orderId); +``` + +## Structs +### Order + +```solidity +struct Order { + uint256 offeringAmount; + address offeringToken; + uint256 askingAmount; + address askingToken; + address requesterAddress; +} +``` + diff --git a/docs/docs/contracts/src/src/swap/Ord_Marketplace.sol/contract.OrdMarketplace.md b/docs/docs/contracts/src/src/swap/Ord_Marketplace.sol/contract.OrdMarketplace.md new file mode 100644 index 00000000..99ee4fa6 --- /dev/null +++ b/docs/docs/contracts/src/src/swap/Ord_Marketplace.sol/contract.OrdMarketplace.md @@ -0,0 +1,215 @@ +# OrdMarketplace +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/swap/Ord_Marketplace.sol) + + +## State Variables +### ordinalSellOrders + +```solidity +mapping(uint256 => OrdinalSellOrder) public ordinalSellOrders; +``` + + +### acceptedOrdinalSellOrders + +```solidity +mapping(uint256 => AcceptedOrdinalSellOrder) public acceptedOrdinalSellOrders; +``` + + +### nextOrdinalId + +```solidity +uint256 nextOrdinalId; +``` + + +### REQUEST_EXPIRATION_SECONDS + +```solidity +uint256 public constant REQUEST_EXPIRATION_SECONDS = 6 hours; +``` + + +### relay + +```solidity +BridgeState.Storage internal relay; +``` + + +### testLightRelay + +```solidity +TestLightRelay internal testLightRelay; +``` + + +## Functions +### constructor + + +```solidity +constructor(IRelay _relay); +``` + +### setRelay + + +```solidity +function setRelay(IRelay _relay) internal; +``` + +### placeOrdinalSellOrder + + +```solidity +function placeOrdinalSellOrder( + OrdinalId calldata ordinalID, + BitcoinTx.UTXO calldata utxo, + address sellToken, + uint256 sellAmount +) public; +``` + +### acceptOrdinalSellOrder + + +```solidity +function acceptOrdinalSellOrder(uint256 id, BitcoinAddress calldata bitcoinAddress) public returns (uint256); +``` + +### proofOrdinalSellOrder + + +```solidity +function proofOrdinalSellOrder(uint256 id, BitcoinTx.Info calldata transaction, BitcoinTx.Proof calldata proof) + public; +``` + +### _checkBitcoinTxOutput + +Checks output script pubkey (recipient address) matches output script. +Reverts if bitcoin address is not found. + + +```solidity +function _checkBitcoinTxOutput(BitcoinAddress storage bitcoinAddress, BitcoinTx.Info calldata transaction) private; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`bitcoinAddress`|`BitcoinAddress`|Recipient's bitcoin address.| +|`transaction`|`BitcoinTx.Info`|Transaction fulfilling the order.| + + +### withdrawOrdinalSellOrder + + +```solidity +function withdrawOrdinalSellOrder(uint256 id) public; +``` + +### cancelAcceptedOrdinalSellOrder + + +```solidity +function cancelAcceptedOrdinalSellOrder(uint256 id) public; +``` + +### getOpenOrdinalSellOrders + + +```solidity +function getOpenOrdinalSellOrders() external view returns (OrdinalSellOrder[] memory, uint256[] memory); +``` + +### getOpenAcceptedOrdinalSellOrders + + +```solidity +function getOpenAcceptedOrdinalSellOrders() + external + view + returns (AcceptedOrdinalSellOrder[] memory, uint256[] memory); +``` + +## Events +### placeOrdinalSellOrderEvent + +```solidity +event placeOrdinalSellOrderEvent(uint256 indexed orderId, OrdinalId ordinalID, address sellToken, uint256 sellAmount); +``` + +### acceptOrdinalSellOrderEvent + +```solidity +event acceptOrdinalSellOrderEvent( + uint256 indexed id, uint256 indexed acceptId, BitcoinAddress bitcoinAddress, address ercToken, uint256 ercAmount +); +``` + +### proofOrdinalSellOrderEvent + +```solidity +event proofOrdinalSellOrderEvent(uint256 id); +``` + +### withdrawOrdinalSellOrderEvent + +```solidity +event withdrawOrdinalSellOrderEvent(uint256 id); +``` + +### cancelAcceptedOrdinalSellOrderEvent + +```solidity +event cancelAcceptedOrdinalSellOrderEvent(uint256 id); +``` + +## Structs +### OrdinalSellOrder + +```solidity +struct OrdinalSellOrder { + OrdinalId ordinalID; + address sellToken; + uint256 sellAmount; + BitcoinTx.UTXO utxo; + address requester; + bool isOrderAccepted; +} +``` + +### AcceptedOrdinalSellOrder + +```solidity +struct AcceptedOrdinalSellOrder { + uint256 orderId; + BitcoinAddress bitcoinAddress; + address ercToken; + uint256 ercAmount; + address requester; + address acceptor; + uint256 acceptTime; +} +``` + +### BitcoinAddress + +```solidity +struct BitcoinAddress { + bytes scriptPubKey; +} +``` + +### OrdinalId + +```solidity +struct OrdinalId { + bytes32 txId; + uint32 index; +} +``` + diff --git a/docs/docs/contracts/src/src/swap/Wrapped.sol/contract.BobWrappedBtc.md b/docs/docs/contracts/src/src/swap/Wrapped.sol/contract.BobWrappedBtc.md new file mode 100644 index 00000000..6fc3a09e --- /dev/null +++ b/docs/docs/contracts/src/src/swap/Wrapped.sol/contract.BobWrappedBtc.md @@ -0,0 +1,36 @@ +# BobWrappedBtc +[Git Source](https://github.com/bob-collective/bob/blob/9dd94230dd2abcab7dfb659e986743be10093c68/src/swap/Wrapped.sol) + +**Inherits:** +ERC20, ERC20Burnable, Ownable + + +## Functions +### constructor + + +```solidity +constructor() ERC20("Bob Wrapped BTC", "zBTC"); +``` + +### sudoMint + + +```solidity +function sudoMint(address to, uint256 amount) public onlyOwner; +``` + +### sudoBurnFrom + + +```solidity +function sudoBurnFrom(address account, uint256 amount) public onlyOwner; +``` + +### sudoTransferFrom + + +```solidity +function sudoTransferFrom(address from, address to, uint256 amount) public onlyOwner; +``` + diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index d0f776fc..4e14c846 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -100,6 +100,12 @@ const config = { position: "left", label: "API", }, + { + type: "docSidebar", + sidebarId: "contractSidebar", + position: "left", + label: "Contracts", + }, { href: DISCORD, label: "Discord", diff --git a/docs/scripts/forge_doc_reformat.py b/docs/scripts/forge_doc_reformat.py new file mode 100644 index 00000000..eadbe040 --- /dev/null +++ b/docs/scripts/forge_doc_reformat.py @@ -0,0 +1,72 @@ +""" +This Python script is designed to modify the Markdown files generated by the 'forge doc' command. +The 'forge doc --out docs/docs/contracts' command doesn't return the docs in the right format for Docusaurus. +Therefore, run this script after running the `forge doc --out docs/docs/contracts` & before publishing the doc to make it compatible with Docusaurus. +""" + +import os +import re + +# Function to parse the Inherits line in the Markdown content +def parse_inherits(md_content): + inherits_match = re.search(r'\*\*Inherits:\*\*\s*([\s\S]*?)\n', md_content) + if inherits_match: + inheritances = inherits_match.group(1).strip() + return inheritances + return None + +# Function to replace paths inside brackets with empty brackets +def replace_paths_with_empty_brackets(line): + def replace_path(match): + default_path = 'docs/docs/src' + path = default_path + match.group(0)[1:-1] # Remove parentheses + start = "docs/docs/src/src/X/X/" + relative_path = os.path.relpath(path, start) + print(f"Original Path: {relative_path}") + return '(' + relative_path + ')' + return re.sub(r'\([^)]+\)', replace_path, line) + +# Function to process a single Markdown file +def process_md_file(file_path): + with open(file_path, 'r') as file: + md_content = file.read() + + # Parse Inherits line + inherits = parse_inherits(md_content) + + # Modify the Inherits line + if inherits: + modified_inherits = replace_paths_with_empty_brackets(inherits) + md_content = md_content.replace(inherits, modified_inherits) + + # Write the modified content back to the original file + with open(file_path, 'w') as file: + file.write(md_content) + + print(f"File modified: {file_path}") + + # Check if the file name is README.md or SUMMARY.md and delete it + file_name = os.path.basename(file_path) + if file_name.lower() in ['readme.md', 'summary.md']: + os.remove(file_path) + print(f"Deleted file: {file_path}") + +# Main function to process all Markdown files in a directory and its subdirectories +def process_all_md_files(directory): + for root, dirs, files in os.walk(directory): + for file in files: + if file.endswith('.md'): + file_path = os.path.join(root, file) + process_md_file(file_path) + +if __name__ == "__main__": + # Get the absolute path of the directory containing the script + script_directory = os.path.dirname(os.path.abspath(__file__)) + + # Specify the directory path relative to the script + directory_path = os.path.join(script_directory, '../docs/contracts/src') + + # Process all Markdown files in the specified directory and its subdirectories + process_all_md_files(directory_path) + + print("All Markdown files in the directory processed.") diff --git a/docs/sidebars.js b/docs/sidebars.js index 88fc7f6e..e3bae94b 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -11,12 +11,49 @@ // @ts-check +const fs = require('fs'); +const contractDir = 'docs/contracts/src/src'; +// Check if the contract directory exists and is a directory +const contractItems = + fs.existsSync(contractDir) && fs.lstatSync(contractDir).isDirectory() + ? fs + .readdirSync(contractDir, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => ({ + type: "category", + label: `${dirent.name.charAt(0).toUpperCase()}${dirent.name.slice( + 1 + )}`, // Capitalize the first letter of the directory name + items: [ + { + type: "autogenerated", + dirName: `contracts/src/src/${dirent.name}`, + }, + ], + })) + : [ + { + type: "category", + label: "Contracts", + items: ["contracts/contract_references"], // Default item when the directory doesn't exist or is not a directory + }, + ]; + + /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { // By default, Docusaurus generates a sidebar from the docs folder structure rootSidebar: [{type: 'autogenerated', dirName: '.'}], learnSidebar: [{type: 'autogenerated', dirName: 'learn'}], buildSidebar: [{type: 'autogenerated', dirName: 'build'}], + contractSidebar: [ + { + type: 'category', + label: 'Contracts', + collapsed: false, + items: contractItems, + } + ], apiSidebar: [ { type: 'category', @@ -57,21 +94,6 @@ const sidebars = { ], } ], - // guideSidebar: [{type: 'autogenerated', dirName: 'guide'}], - // runSidebar: [{type: 'autogenerated', dirName: 'run'}], - - // But you can create a sidebar manually - /* - tutorialSidebar: [ - 'intro', - 'hello', - { - type: 'category', - label: 'Tutorial', - items: ['tutorial-basics/create-a-document'], - }, - ], - */ }; module.exports = sidebars; diff --git a/src/bridge/BitcoinTx.sol b/src/bridge/BitcoinTx.sol index b8a147cb..7f6850b9 100644 --- a/src/bridge/BitcoinTx.sol +++ b/src/bridge/BitcoinTx.sol @@ -13,71 +13,69 @@ import "./BridgeState.sol"; /// @title Bitcoin transaction /// @notice Allows to reference Bitcoin raw transaction in Solidity. /// @dev See https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format -/// -/// Raw Bitcoin transaction data: -/// -/// | Bytes | Name | BTC type | Description | -/// |--------|--------------|------------------------|---------------------------| -/// | 4 | version | int32_t (LE) | TX version number | -/// | varies | tx_in_count | compactSize uint (LE) | Number of TX inputs | -/// | varies | tx_in | txIn[] | TX inputs | -/// | varies | tx_out_count | compactSize uint (LE) | Number of TX outputs | -/// | varies | tx_out | txOut[] | TX outputs | -/// | 4 | lock_time | uint32_t (LE) | Unix time or block number | -/// +// Raw Bitcoin transaction data: +// | Bytes | Name | BTC type | Description | +// |--------|--------------|------------------------|---------------------------| +// | 4 | version | int32_t (LE) | TX version number | +// | varies | tx_in_count | compactSize uint (LE) | Number of TX inputs | +// | varies | tx_in | txIn[] | TX inputs | +// | varies | tx_out_count | compactSize uint (LE) | Number of TX outputs | +// | varies | tx_out | txOut[] | TX outputs | +// | 4 | lock_time | uint32_t (LE) | Unix time or block number | // -/// Non-coinbase transaction input (txIn): -/// -/// | Bytes | Name | BTC type | Description | -/// |--------|------------------|------------------------|---------------------------------------------| -/// | 36 | previous_output | outpoint | The previous outpoint being spent | -/// | varies | script_bytes | compactSize uint (LE) | The number of bytes in the signature script | -/// | varies | signature_script | char[] | The signature script, empty for P2WSH | -/// | 4 | sequence | uint32_t (LE) | Sequence number | -/// -/// -/// The reference to transaction being spent (outpoint): -/// -/// | Bytes | Name | BTC type | Description | -/// |-------|-------|---------------|------------------------------------------| -/// | 32 | hash | char[32] | Hash of the transaction to spend | -/// | 4 | index | uint32_t (LE) | Index of the specific output from the TX | -/// -/// -/// Transaction output (txOut): -/// -/// | Bytes | Name | BTC type | Description | -/// |--------|-----------------|-----------------------|--------------------------------------| -/// | 8 | value | int64_t (LE) | Number of satoshis to spend | -/// | 1+ | pk_script_bytes | compactSize uint (LE) | Number of bytes in the pubkey script | -/// | varies | pk_script | char[] | Pubkey script | -/// -/// compactSize uint format: -/// -/// | Value | Bytes | Format | -/// |-----------------------------------------|-------|----------------------------------------------| -/// | >= 0 && <= 252 | 1 | uint8_t | -/// | >= 253 && <= 0xffff | 3 | 0xfd followed by the number as uint16_t (LE) | -/// | >= 0x10000 && <= 0xffffffff | 5 | 0xfe followed by the number as uint32_t (LE) | -/// | >= 0x100000000 && <= 0xffffffffffffffff | 9 | 0xff followed by the number as uint64_t (LE) | -/// -/// (*) compactSize uint is often references as VarInt) -/// -/// Coinbase transaction input (txIn): -/// -/// | Bytes | Name | BTC type | Description | -/// |--------|------------------|------------------------|---------------------------------------------| -/// | 32 | hash | char[32] | A 32-byte 0x0 null (no previous_outpoint) | -/// | 4 | index | uint32_t (LE) | 0xffffffff (no previous_outpoint) | -/// | varies | script_bytes | compactSize uint (LE) | The number of bytes in the coinbase script | -/// | varies | height | char[] | The block height of this block (BIP34) (*) | -/// | varies | coinbase_script | none | Arbitrary data, max 100 bytes | -/// | 4 | sequence | uint32_t (LE) | Sequence number -/// -/// (*) Uses script language: starts with a data-pushing opcode that indicates how many bytes to push to -/// the stack followed by the block height as a little-endian unsigned integer. This script must be as -/// short as possible, otherwise it may be rejected. The data-pushing opcode will be 0x03 and the total -/// size four bytes until block 16,777,216 about 300 years from now. +// Non-coinbase transaction input (txIn): +// +// | Bytes | Name | BTC type | Description | +// |--------|------------------|------------------------|---------------------------------------------| +// | 36 | previous_output | outpoint | The previous outpoint being spent | +// | varies | script_bytes | compactSize uint (LE) | The number of bytes in the signature script | +// | varies | signature_script | char[] | The signature script, empty for P2WSH | +// | 4 | sequence | uint32_t (LE) | Sequence number | +// +// +// The reference to transaction being spent (outpoint): +// +// | Bytes | Name | BTC type | Description | +// |-------|-------|---------------|------------------------------------------| +// | 32 | hash | char[32] | Hash of the transaction to spend | +// | 4 | index | uint32_t (LE) | Index of the specific output from the TX | +// +// +// Transaction output (txOut): +// +// | Bytes | Name | BTC type | Description | +// |--------|-----------------|-----------------------|--------------------------------------| +// | 8 | value | int64_t (LE) | Number of satoshis to spend | +// | 1+ | pk_script_bytes | compactSize uint (LE) | Number of bytes in the pubkey script | +// | varies | pk_script | char[] | Pubkey script | +// +// compactSize uint format: +// +// | Value | Bytes | Format | +// |-----------------------------------------|-------|----------------------------------------------| +// | >= 0 && <= 252 | 1 | uint8_t | +// | >= 253 && <= 0xffff | 3 | 0xfd followed by the number as uint16_t (LE) | +// | >= 0x10000 && <= 0xffffffff | 5 | 0xfe followed by the number as uint32_t (LE) | +// | >= 0x100000000 && <= 0xffffffffffffffff | 9 | 0xff followed by the number as uint64_t (LE) | +// +// (*) compactSize uint is often references as VarInt) +// +// Coinbase transaction input (txIn): +// +// | Bytes | Name | BTC type | Description | +// |--------|------------------|------------------------|---------------------------------------------| +// | 32 | hash | char[32] | A 32-byte 0x0 null (no previous_outpoint) | +// | 4 | index | uint32_t (LE) | 0xffffffff (no previous_outpoint) | +// | varies | script_bytes | compactSize uint (LE) | The number of bytes in the coinbase script | +// | varies | height | char[] | The block height of this block (BIP34) (*) | +// | varies | coinbase_script | none | Arbitrary data, max 100 bytes | +// | 4 | sequence | uint32_t (LE) | Sequence number +// +// (*) Uses script language: starts with a data-pushing opcode that indicates how many bytes to push to +// the stack followed by the block height as a little-endian unsigned integer. This script must be as +// short as possible, otherwise it may be rejected. The data-pushing opcode will be 0x03 and the total +// size four bytes until block 16,777,216 about 300 years from now. + library BitcoinTx { using BTCUtils for bytes; using BTCUtils for uint256;