Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/TBC20/build/src/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export class Token extends Contract {
}
if (this.amount >= amount) {
this.amount -= amount;
return new Token(to, amount, this.name, this.symbol);
const ctor = this.constructor;
return new ctor(to, amount, this.name, this.symbol);
}
throw new Error('Insufficient funds');
}
Expand Down
6 changes: 3 additions & 3 deletions packages/TBC20/src/token.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


type Constructor<T> = new (...args: any[]) => T

export class Token extends Contract {
amount: bigint
Expand All @@ -20,7 +19,8 @@ export class Token extends Contract {
if (this.amount >= amount) {
// Send partial amount in a new object
this.amount -= amount
return new Token(to, amount, this.name, this.symbol)
const ctor = this.constructor as Constructor<this>
return new ctor(to, amount, this.name, this.symbol)
}
throw new Error('Insufficient funds')
}
Expand Down
5 changes: 2 additions & 3 deletions packages/TBC20/test/token.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import { expect } from 'chai'
import { Computer } from '@bitcoin-computer/lib'
import dotenv from 'dotenv'
Expand Down Expand Up @@ -115,8 +114,8 @@ describe('Token', async () => {
expect(token2._id).eq(token2After._id)
expect(token2._rev).not.eq(token2After._rev)

expect(await sender.query({ ids: [token1._id] })).deep.eq([token1._rev])
expect(await sender.query({ ids: [token2._id] })).deep.eq([token2After._rev])
expect(await sender.last(token1._id)).deep.eq(token1._rev)
expect(await sender.last(token2._id)).deep.eq(token2After._rev)
})

it('Sender merges their two tokens', async () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/TBC721/build/src/nft.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ export class NftHelper {
return objects.length;
}
async ownersOf(tokenId) {
const [rev] = await this.computer.query({ ids: [tokenId] });
const rev = await this.computer.last(tokenId);
const obj = await this.computer.sync(rev);
return obj._owners;
}
async transfer(to, tokenId) {
const [rev] = await this.computer.query({ ids: [tokenId] });
const rev = await this.computer.last(tokenId);
const obj = await this.computer.sync(rev);
await obj.transfer(to);
}
Expand Down
6 changes: 2 additions & 4 deletions packages/TBC721/src/nft.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


export class NFT extends Contract {
name: string
artist: string
Expand Down Expand Up @@ -60,13 +58,13 @@ export class NftHelper implements ITBC721 {
}

async ownersOf(tokenId: string): Promise<string[]> {
const [rev] = await this.computer.query({ ids: [tokenId] })
const rev = await this.computer.last(tokenId)
const obj = await this.computer.sync(rev)
return obj._owners
}

async transfer(to: string, tokenId: string): Promise<void> {
const [rev] = await this.computer.query({ ids: [tokenId] })
const rev = await this.computer.last(tokenId)
const obj = await this.computer.sync(rev)
await obj.transfer(to)
}
Expand Down
3 changes: 0 additions & 3 deletions packages/chess-contracts/build/src/chess.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/chess-contracts/build/src/chess.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/chess-contracts/build/src/main.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion packages/chess-contracts/build/test/chess-contract.test.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions packages/components/built/Auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { initFlowbite } from 'flowbite';
import { HiRefresh } from 'react-icons/hi';
import { useUtilsComponents } from './UtilsContext';
import { Modal } from './Modal';
import { getEnv } from './common/utils';
import { isInEnv } from './common/utils';
const pathPattern = /^(m\/)?(\d+'?\/)*\d+'?$/;
function isLoggedIn() {
return !!localStorage.getItem('BIP_39_KEY');
Expand Down Expand Up @@ -41,19 +41,19 @@ function getPath({ chain, network }) {
}
function loggedOutConfiguration() {
return {
chain: getEnv('CHAIN'),
network: getEnv('NETWORK'),
url: getEnv('URL'),
path: getEnv('PATH'),
chain: isInEnv('CHAIN'),
network: isInEnv('NETWORK'),
url: isInEnv('URL'),
path: isInEnv('PATH'),
};
}
function loggedInConfiguration() {
return {
mnemonic: localStorage.getItem('BIP_39_KEY'),
chain: (localStorage.getItem('CHAIN') || getEnv('CHAIN')),
network: (localStorage.getItem('NETWORK') || getEnv('NETWORK')),
url: localStorage.getItem('URL') || getEnv('URL'),
path: localStorage.getItem('PATH') || getEnv('PATH'),
chain: (localStorage.getItem('CHAIN') || isInEnv('CHAIN')),
network: (localStorage.getItem('NETWORK') || isInEnv('NETWORK')),
url: localStorage.getItem('URL') || isInEnv('URL'),
path: localStorage.getItem('PATH') || isInEnv('PATH'),
};
}
function getComputer(options = {}) {
Expand Down Expand Up @@ -120,15 +120,15 @@ function LoginButton({ mnemonic, chain, network, path, url, urlInputRef }) {
}
function LoginForm() {
const [mnemonic, setMnemonic] = useState(() => new Computer().getMnemonic());
const [chain, setChain] = useState(getEnv('CHAIN'));
const [network, setNetwork] = useState(getEnv('NETWORK'));
const [url, setUrl] = useState(getEnv('URL') || 'http://localhost:1031');
const [chain, setChain] = useState(isInEnv('CHAIN'));
const [network, setNetwork] = useState(isInEnv('NETWORK'));
const [url, setUrl] = useState(isInEnv('URL') || 'http://localhost:1031');
const urlInputRef = useRef(null);
const [path, setPath] = useState(getEnv('PATH') || getPath({ chain, network }));
const [path, setPath] = useState(isInEnv('PATH') || getPath({ chain, network }));
useEffect(() => {
initFlowbite();
}, []);
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "max-w-sm mx-auto p-4 md:p-5 space-y-4", children: _jsx("form", { className: "space-y-6", children: _jsxs("div", { children: [_jsx(MnemonicInput, { mnemonic: mnemonic, setMnemonic: setMnemonic }), !getEnv('CHAIN') && _jsx(ChainInput, { chain: chain, setChain: setChain }), !getEnv('NETWORK') && _jsx(NetworkInput, { network: network, setNetwork: setNetwork }), !getEnv('URL') && _jsx(UrlInput, { url: url || '', setUrl: setUrl }), !getEnv('PATH') && _jsx(PathInput, { path: path, setPath: setPath })] }) }) }), _jsx("div", { className: "max-w-sm mx-auto flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600", children: _jsx(LoginButton, { mnemonic: mnemonic, chain: chain, network: network, url: url, path: path, urlInputRef: urlInputRef }) })] }));
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "max-w-sm mx-auto p-4 md:p-5 space-y-4", children: _jsx("form", { className: "space-y-6", children: _jsxs("div", { children: [_jsx(MnemonicInput, { mnemonic: mnemonic, setMnemonic: setMnemonic }), !isInEnv('CHAIN') && _jsx(ChainInput, { chain: chain, setChain: setChain }), !isInEnv('NETWORK') && _jsx(NetworkInput, { network: network, setNetwork: setNetwork }), !isInEnv('URL') && _jsx(UrlInput, { url: url || '', setUrl: setUrl }), !isInEnv('PATH') && _jsx(PathInput, { path: path, setPath: setPath })] }) }) }), _jsx("div", { className: "max-w-sm mx-auto flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600", children: _jsx(LoginButton, { mnemonic: mnemonic, chain: chain, network: network, url: url, path: path, urlInputRef: urlInputRef }) })] }));
}
function LoginModal() {
return _jsx(Modal.Component, { title: "Sign in", content: LoginForm, id: "sign-in-modal", hideClose: true });
Expand Down
4 changes: 2 additions & 2 deletions packages/components/built/Wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Auth } from './Auth';
import { Drawer } from './Drawer';
import { UtilsContext } from './UtilsContext';
import { ComputerContext } from './ComputerContext';
import { getEnv, bigIntToStr } from './common/utils';
import { isInEnv, bigIntToStr } from './common/utils';
const Balance = ({ computer, modSpecs, isOpen, }) => {
const [balance, setBalance] = useState(0n);
const [, setChain] = useState(localStorage.getItem('CHAIN') || 'LTC');
Expand Down Expand Up @@ -72,7 +72,7 @@ const Path = ({ computer }) => (_jsxs("div", { className: "mb-4", children: [_js
const LogOut = () => (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-6", children: [_jsx("h6", { className: "text-lg font-bold dark:text-white", children: "Log out" }), _jsx("p", { className: "mb-1 text-sm text-gray-500 dark:text-gray-400", children: "Logging out will delete your mnemonic. Make sure to write it down." })] }), _jsx("div", { className: "grid grid-cols-2 gap-4", children: _jsx("button", { onClick: Auth.logout, className: "rounded-lg border border-gray-200 bg-white px-4 py-2 text-center text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700", children: "Log out" }) })] }));
export function Wallet({ modSpecs }) {
const computer = useContext(ComputerContext);
const Content = ({ isOpen }) => (_jsxs(_Fragment, { children: [_jsx("h4", { className: "text-2xl font-bold dark:text-white", children: "Wallet" }), _jsx(Balance, { computer: computer, modSpecs: modSpecs || [], isOpen: isOpen }), _jsx(Address, { computer: computer }), _jsx(PublicKey, { computer: computer }), _jsx(Mnemonic, { computer: computer }), !getEnv('CHAIN') && _jsx(Chain, { computer: computer }), !getEnv('NETWORK') && _jsx(Network, { computer: computer }), !getEnv('URL') && _jsx(Url, { computer: computer }), !getEnv('PATH') && _jsx(Path, { computer: computer }), _jsx("hr", { className: "h-px my-6 bg-gray-200 border-0 dark:bg-gray-700" }), _jsx(LogOut, {})] }));
const Content = ({ isOpen }) => (_jsxs(_Fragment, { children: [_jsx("h4", { className: "text-2xl font-bold dark:text-white", children: "Wallet" }), _jsx(Balance, { computer: computer, modSpecs: modSpecs || [], isOpen: isOpen }), _jsx(Address, { computer: computer }), _jsx(PublicKey, { computer: computer }), _jsx(Mnemonic, { computer: computer }), !isInEnv('CHAIN') && _jsx(Chain, { computer: computer }), !isInEnv('NETWORK') && _jsx(Network, { computer: computer }), !isInEnv('URL') && _jsx(Url, { computer: computer }), !isInEnv('PATH') && _jsx(Path, { computer: computer }), _jsx("hr", { className: "h-px my-6 bg-gray-200 border-0 dark:bg-gray-700" }), _jsx(LogOut, {})] }));
return _jsx(Drawer.Component, { Content: Content, id: "wallet-drawer" });
}
export const WalletComponents = {
Expand Down
2 changes: 1 addition & 1 deletion packages/components/built/common/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export declare const capitalizeFirstLetter: (string: string) => string;
export declare function isValidRevString(outId: string): boolean;
export declare function isValidRev(value: string | number | boolean | null | undefined): boolean;
export declare const sleep: (ms: number) => Promise<void>;
export declare function getEnv(name: string): any;
export declare function isInEnv(name: string): any;
export declare function bigIntToStr(a: bigint): string;
export declare function strToBigInt(a: string): bigint;
export {};
2 changes: 1 addition & 1 deletion packages/components/built/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function isValidRev(value) {
export const sleep = (ms) => new Promise((resolve) => {
setTimeout(resolve, ms);
});
export function getEnv(name) {
export function isInEnv(name) {
return ((typeof process !== 'undefined' && process.env[`REACT_APP_${name}`]) ||
(import.meta.env && import.meta.env[`VITE_${name}`]));
}
Expand Down
5 changes: 2 additions & 3 deletions packages/components/src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

type Json = JBasic | JObject | JArray
type JBasic = undefined | null | boolean | number | string | symbol | bigint
type JArray = Json[]
Expand Down Expand Up @@ -44,7 +43,7 @@ export const jsonMap =
export const strip = (value: Json): Json => {
if (isJBasic(value)) return value
if (isJArray(value)) return value.map(strip)

const { _id, _root, _rev, _satoshis, _owners, ...rest } = value
return rest
}
Expand All @@ -69,7 +68,7 @@ export const sleep = (ms: number): Promise<void> =>
setTimeout(resolve, ms)
})

export function getEnv(name: string) {
export function getEnv(name: string): string {
return (
(typeof process !== 'undefined' && process.env[`REACT_APP_${name}`]) ||
(import.meta.env && import.meta.env[`VITE_${name}`])
Expand Down
14 changes: 13 additions & 1 deletion packages/docs/fees.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ Miner fees are paid to the miners as usual. You can configure the `satsPerByte`

We charge a small fee for using the protocol. The fee is independent of the amount being sent or the size of the transaction; it depends only on the size of the metadata:

### How It Works

1. Each Bitcoin Computer transaction encodes a JavaScript expression and other metadata like the environment into bare multisig scripts, each carrying the smallest allowable (non-dust) output.
2. To ensure these outputs don’t bloat the UTXO set, we include a public key that only the company developing the Bitcoin Computer (BCDB Inc.) can spend.
3. When on-chain fees fall low enough, BCDB Inc. will collect these dust-level outputs to fund continued development.

This approach keeps your UTXO set clean while sustainably supporting the Bitcoin Computer platform.

### Fee Calculation

Fee calculation is very simple:

!!! Fee Estimate
The protocol fees is one dust amount for every 66 bytes of metadata.
!!!
Expand All @@ -26,4 +38,4 @@ The table below gives an overview of the dust amounts across different blockchai
| DOGE | ~ 0.01 DOGE | ~ $0.00150 |
| PEPE | ~ 0.01 PEPE | ~ $0.00000005 |

The actual fee might vary slightly from the estimates given above. In the following we explain how the fee is calculated in detail. Recall from the Section called [Tx Format](./format.md) that the metadata is encoded in bare multisig scripts. These scripts can contain up to three public keys. We use on of these to be able to spend the dust and the other 1 or 2 public keys encode the data. Therefore the fee to store two public keys worth of data (66 bytes) is one dust amount.
We are able to recover the majority of these dust amounts, for example, on Litecoin the user pays ca $0.007 in dust fees per metadata script and we can recover about $0.005.
8 changes: 7 additions & 1 deletion packages/lib/computer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ declare class RestClient {
height(): Promise<number>
next(rev: string): Promise<string | undefined>
prev(rev: string): Promise<string | undefined>
last(rev: string): Promise<string>
first(rev: string): Promise<string>
}

declare class Wallet {
Expand Down Expand Up @@ -512,7 +514,7 @@ declare class Computer {
toScriptPubKey(publicKeys?: string[]): Buffer | undefined
static lockdown(opts?: any): void
delete(inRevs: string[]): Promise<string>
isUnspent(rev: string): Promise<boolean>
isUTXO(rev: string): Promise<boolean>
next(rev: string): Promise<string | undefined>
prev(rev: string): Promise<string | undefined>
subscribe(
Expand All @@ -529,6 +531,10 @@ declare class Computer {
getLatestRev(id: string): Promise<string>
idsToRevs(ids: string[]): Promise<string[]>
getMinimumFees(): number
next(rev: string): Promise<string | undefined>
prev(rev: string): Promise<string | undefined>
last(rev: string): Promise<string>
first(rev: string): Promise<string>
}

export { Computer, Contract, Mock, Transaction }
Loading