add buckets list page
This commit is contained in:
parent
216fa8aed8
commit
de05b94699
|
@ -0,0 +1,134 @@
|
|||
import { RootState } from '../reducers';
|
||||
import { Dispatch } from 'redux';
|
||||
import { bucketsAddresses } from "../config";
|
||||
import Bucket from '../contracts/Bucket.json';
|
||||
import { newBucketContract } from "../utils";
|
||||
import { TokenType } from "../reducers/buckets";
|
||||
import IERC20Detailed from '../contracts/IERC20Detailed.json';
|
||||
import { AbiItem } from "web3-utils";
|
||||
import { config } from "../config";
|
||||
import { TokenDetails, ERC20Details } from "../reducers/buckets";
|
||||
|
||||
export const BUCKETS_LOADING = "BUCKETS_LOADING";
|
||||
export interface BucketsLoadingAction {
|
||||
type: typeof BUCKETS_LOADING
|
||||
recipientAddress: string
|
||||
}
|
||||
|
||||
export const BUCKETS_LOADING_BUCKET = "BUCKETS_LOADING_BUCKET";
|
||||
export interface BucketsLoadingBucketAction {
|
||||
type: typeof BUCKETS_LOADING_BUCKET
|
||||
recipientAddress: string
|
||||
bucketAddress: string
|
||||
}
|
||||
|
||||
export const BUCKETS_REDEEMABLE_TOKEN_ADDRESS_LOADED = "BUCKETS_REDEEMABLE_TOKEN_ADDRESS_LOADED";
|
||||
export interface BucketsRedeemableTokenAddressLoadedAction {
|
||||
type: typeof BUCKETS_REDEEMABLE_TOKEN_ADDRESS_LOADED
|
||||
recipientAddress: string
|
||||
bucketAddress: string
|
||||
tokenAddress: string
|
||||
}
|
||||
|
||||
export const BUCKETS_REDEEMABLE_TOKEN_TYPE_LOADED = "BUCKETS_REDEEMABLE_TOKEN_TYPE_LOADED";
|
||||
export interface BucketsRedeemableTokenTypeLoadedAction {
|
||||
type: typeof BUCKETS_REDEEMABLE_TOKEN_TYPE_LOADED
|
||||
recipientAddress: string
|
||||
bucketAddress: string
|
||||
tokenType: TokenType
|
||||
}
|
||||
|
||||
export const BUCKETS_REDEEMABLE_TOKEN_DETAILS_LOADED = "BUCKETS_REDEEMABLE_TOKEN_DETAILS_LOADED";
|
||||
export interface BucketsRedeemableTokenDetailsLoadedAction {
|
||||
type: typeof BUCKETS_REDEEMABLE_TOKEN_DETAILS_LOADED
|
||||
recipientAddress: string
|
||||
bucketAddress: string
|
||||
tokenDetails: TokenDetails
|
||||
}
|
||||
|
||||
export type BucketsActions =
|
||||
BucketsLoadingAction |
|
||||
BucketsLoadingBucketAction |
|
||||
BucketsRedeemableTokenAddressLoadedAction |
|
||||
BucketsRedeemableTokenTypeLoadedAction |
|
||||
BucketsRedeemableTokenDetailsLoadedAction;
|
||||
|
||||
export const loadingBuckets = (recipientAddress: string): BucketsLoadingAction => ({
|
||||
type: BUCKETS_LOADING,
|
||||
recipientAddress,
|
||||
});
|
||||
|
||||
export const loadingBucket = (recipientAddress: string, bucketAddress: string): BucketsLoadingBucketAction => ({
|
||||
type: BUCKETS_LOADING_BUCKET,
|
||||
recipientAddress,
|
||||
bucketAddress,
|
||||
});
|
||||
|
||||
export const tokenAddressLoaded = (recipientAddress: string, bucketAddress: string, tokenAddress: string): BucketsRedeemableTokenAddressLoadedAction => ({
|
||||
type: BUCKETS_REDEEMABLE_TOKEN_ADDRESS_LOADED,
|
||||
recipientAddress,
|
||||
bucketAddress,
|
||||
tokenAddress,
|
||||
});
|
||||
|
||||
export const tokenTypeLoaded = (recipientAddress: string, bucketAddress: string, tokenType: TokenType): BucketsRedeemableTokenTypeLoadedAction => ({
|
||||
type: BUCKETS_REDEEMABLE_TOKEN_TYPE_LOADED,
|
||||
recipientAddress,
|
||||
bucketAddress,
|
||||
tokenType,
|
||||
});
|
||||
|
||||
export const tokenDetailsLoaded = (recipientAddress: string, bucketAddress: string, tokenDetails: TokenDetails): BucketsRedeemableTokenDetailsLoadedAction => ({
|
||||
type: BUCKETS_REDEEMABLE_TOKEN_DETAILS_LOADED,
|
||||
recipientAddress,
|
||||
bucketAddress,
|
||||
tokenDetails,
|
||||
});
|
||||
|
||||
export const loadBuckets = (recipientAddress: string) => {
|
||||
return (dispatch: Dispatch, getState: () => RootState) => {
|
||||
dispatch(loadingBuckets(recipientAddress));
|
||||
|
||||
const addresses = bucketsAddresses();
|
||||
addresses.forEach((bucketAddress) => {
|
||||
dispatch<any>(loadBucket(recipientAddress, bucketAddress));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const loadBucket = (recipientAddress: string, bucketAddress: string) => {
|
||||
return async (dispatch: Dispatch, getState: () => RootState) => {
|
||||
dispatch(loadingBucket(recipientAddress, bucketAddress));
|
||||
const bucket = newBucketContract(bucketAddress);
|
||||
|
||||
const _type = await bucket.methods.bucketType().call();
|
||||
const type = _type === "20" ? "erc20" : "ntf";
|
||||
dispatch(tokenTypeLoaded(recipientAddress, bucketAddress, type as TokenType));
|
||||
|
||||
const tokenAddress = await bucket.methods.tokenAddress().call();
|
||||
dispatch(tokenAddressLoaded(recipientAddress, bucketAddress, tokenAddress));
|
||||
|
||||
//FIXME: catch possible error
|
||||
const tokenDetails = await loadERC20Token(tokenAddress);
|
||||
dispatch(tokenDetailsLoaded(recipientAddress, bucketAddress, tokenDetails));
|
||||
}
|
||||
}
|
||||
|
||||
const loadERC20Token = (address: string): Promise<ERC20Details> => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const erc20Abi = IERC20Detailed.abi as AbiItem[];
|
||||
const erc20 = new config.web3!.eth.Contract(erc20Abi, address);
|
||||
try {
|
||||
const name = await erc20.methods.name().call();
|
||||
const symbol = await erc20.methods.symbol().call();
|
||||
const decimals = parseInt(await erc20.methods.decimals().call());
|
||||
resolve({
|
||||
name,
|
||||
symbol,
|
||||
decimals,
|
||||
});
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
import { RootState } from '../reducers';
|
||||
import { config } from "../config";
|
||||
import { Dispatch } from 'redux';
|
||||
import { newBucketContract } from "./redeemable";
|
||||
import { sha3 } from "web3-utils";
|
||||
import { recoverTypedSignature } from 'eth-sig-util';
|
||||
import { Web3Type } from "../actions/web3";
|
||||
import { KECCAK_EMPTY_STRING } from '../utils';
|
||||
import { KECCAK_EMPTY_STRING, newBucketContract} from '../utils';
|
||||
import { debug } from "./debug";
|
||||
|
||||
interface RedeemMessage {
|
||||
|
|
|
@ -2,14 +2,16 @@ import { RootState } from '../reducers';
|
|||
import ERC20BucketFactory from '../contracts/ERC20BucketFactory.json';
|
||||
import NFTBucketFactory from '../contracts/NFTBucketFactory.json';
|
||||
import ERC20Bucket from '../contracts/ERC20Bucket.json';
|
||||
import Bucket from '../contracts/Bucket.json';
|
||||
import IERC20Detailed from '../contracts/IERC20Detailed.json';
|
||||
import IERC721Metadata from '../contracts/IERC721Metadata.json';
|
||||
import { config } from "../config";
|
||||
import { Dispatch } from 'redux';
|
||||
import { ZERO_ADDRESS } from "../utils";
|
||||
import { debug } from "./debug";
|
||||
import { AbiItem } from "web3-utils";
|
||||
import {
|
||||
ZERO_ADDRESS ,
|
||||
newBucketContract,
|
||||
} from "../utils";
|
||||
import { debug } from "./debug";
|
||||
|
||||
interface ContractSpecs {
|
||||
networks: {
|
||||
|
@ -178,18 +180,6 @@ export const tokenMetadataLoaded = (tokenAddress: string, recipient: string, met
|
|||
metadata,
|
||||
});
|
||||
|
||||
export const newBucketContract = (address: string) => {
|
||||
const bucketAbi = Bucket.abi as AbiItem[];
|
||||
const bucket = new config.web3!.eth.Contract(bucketAbi, address);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
export const newERC20BucketContract = (address: string) => {
|
||||
const bucketAbi = ERC20Bucket.abi as AbiItem[];
|
||||
const bucket = new config.web3!.eth.Contract(bucketAbi, address);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
export const loadRedeemable = (bucketAddress: string, recipientAddress: string) => {
|
||||
return async (dispatch: Dispatch, getState: () => RootState) => {
|
||||
const networkID = getState().web3.networkID!;
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { RootState } from '../reducers';
|
||||
import { useRouteMatch } from 'react-router-dom';
|
||||
import {
|
||||
shallowEqual,
|
||||
useSelector,
|
||||
useDispatch,
|
||||
} from 'react-redux';
|
||||
import { recipientBucketsPath } from '../config';
|
||||
import { loadBuckets } from "../actions/buckets";
|
||||
import { ERC20Details } from "../reducers/buckets";
|
||||
|
||||
interface BuckestListItemProps {
|
||||
bucketAddress: string
|
||||
}
|
||||
|
||||
const BuckestListItem = (ownProps: BuckestListItemProps) => {
|
||||
const props = useSelector((state: RootState) => {
|
||||
const redeemable = state.buckets.redeemables[ownProps.bucketAddress];
|
||||
if (redeemable === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
bucketAddress: ownProps.bucketAddress,
|
||||
loading: redeemable.loading,
|
||||
tokenAddress: redeemable.tokenAddress,
|
||||
tokenType: redeemable.tokenType,
|
||||
tokenDetails: redeemable.tokenDetails,
|
||||
}
|
||||
}, shallowEqual);
|
||||
|
||||
if (props === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <div>
|
||||
Bucket: {props.bucketAddress}
|
||||
{props.loading && <span> (LOADING...)</span>}
|
||||
<br />
|
||||
Token address: {props.tokenAddress}
|
||||
<br />
|
||||
Type: {props.tokenType}
|
||||
<br />
|
||||
{props.tokenDetails && <>
|
||||
Symbol: {props.tokenDetails.symbol}
|
||||
<br />
|
||||
Name: {props.tokenDetails.name}
|
||||
</>}
|
||||
<hr />
|
||||
</div>;
|
||||
}
|
||||
|
||||
interface URLParams {
|
||||
recipientAddress: string
|
||||
}
|
||||
|
||||
export default function(ownProps: any) {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const match = useRouteMatch<URLParams>({
|
||||
path: recipientBucketsPath,
|
||||
exact: true,
|
||||
});
|
||||
|
||||
if (match === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const recipientAddress = match.params.recipientAddress;
|
||||
|
||||
const props = useSelector((state: RootState) => {
|
||||
return {
|
||||
loading: state.buckets.loading,
|
||||
buckets: state.buckets.buckets,
|
||||
}
|
||||
}, shallowEqual);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("loading buckets")
|
||||
dispatch(loadBuckets(recipientAddress));
|
||||
}, [dispatch, recipientAddress]); // FIXME: unload buckets
|
||||
|
||||
return <div>
|
||||
<div>buckets for {recipientAddress}</div>
|
||||
<ul>
|
||||
{props.buckets.map(bucketAddress => <li key={bucketAddress}>
|
||||
<BuckestListItem bucketAddress={bucketAddress} />
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
|
@ -40,7 +40,7 @@ import {
|
|||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
|
||||
const buckerErrorMessage = (error: RedeemableErrors): string => {
|
||||
const bucketErrorMessage = (error: RedeemableErrors): string => {
|
||||
switch (error.type) {
|
||||
case ERROR_LOADING_REDEEMABLE:
|
||||
return "couldn't load redeemable";
|
||||
|
@ -116,7 +116,7 @@ export default function(ownProps: any) {
|
|||
|
||||
if (props.error) {
|
||||
return <div className={classNames({ paper: true, error: true })}>
|
||||
{buckerErrorMessage(props.error)}
|
||||
{bucketErrorMessage(props.error)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,4 +8,14 @@ export const config: Config = {
|
|||
web3: undefined
|
||||
};
|
||||
|
||||
export const recipientBucketsPath = "/recipients/:recipientAddress/buckets";
|
||||
export const redeemablePath = "/buckets/:bucketAddress/redeemables/:recipientAddress";
|
||||
|
||||
export const bucketsAddresses = (): Array<string> => {
|
||||
const s = process.env.REACT_APP_BUCKETS;
|
||||
if (s === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return s.split(",").map((a) => a.trim());
|
||||
}
|
||||
|
|
|
@ -12,7 +12,11 @@ import ErrorBoundary from './components/ErrorBoundary';
|
|||
import Layout from './components/Layout';
|
||||
import Home from './components/Home';
|
||||
import Redeemable from './components/Redeemable';
|
||||
import { redeemablePath } from './config';
|
||||
import RecipientBuckets from './components/RecipientBuckets';
|
||||
import {
|
||||
recipientBucketsPath,
|
||||
redeemablePath,
|
||||
} from './config';
|
||||
|
||||
const logger: Middleware = ({ getState }: MiddlewareAPI) => (next: Dispatch) => action => {
|
||||
console.log('dispatch', action);
|
||||
|
@ -49,6 +53,7 @@ ReactDOM.render(
|
|||
<ConnectedRouter history={history}>
|
||||
<Switch>
|
||||
<Route exact path="/"><Home /></Route>
|
||||
<Route exact path={recipientBucketsPath}><RecipientBuckets /></Route>
|
||||
<Route exact path={redeemablePath}><Redeemable /></Route>
|
||||
<Route render={() => "page not found"} />
|
||||
</Switch>
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
import {
|
||||
BucketsActions,
|
||||
BUCKETS_LOADING,
|
||||
BUCKETS_LOADING_BUCKET,
|
||||
BUCKETS_REDEEMABLE_TOKEN_ADDRESS_LOADED,
|
||||
BUCKETS_REDEEMABLE_TOKEN_TYPE_LOADED,
|
||||
BUCKETS_REDEEMABLE_TOKEN_DETAILS_LOADED,
|
||||
} from '../actions/buckets';
|
||||
|
||||
export type TokenType = "erc20" | "nft";
|
||||
|
||||
export interface ERC20Details {
|
||||
name: string
|
||||
symbol: string
|
||||
decimals: number
|
||||
}
|
||||
|
||||
export type TokenDetails = ERC20Details;
|
||||
|
||||
interface Redeemable {
|
||||
bucketAddress: string
|
||||
tokenAddress: undefined | string
|
||||
loading: boolean
|
||||
tokenType: undefined | TokenType
|
||||
tokenDetails: undefined | ERC20Details
|
||||
value: undefined | string
|
||||
}
|
||||
|
||||
export interface BucketsState {
|
||||
recipientAddress: undefined | string
|
||||
loading: boolean
|
||||
buckets: Array<string>
|
||||
redeemables: {
|
||||
[bucketAddress: string]: Redeemable
|
||||
}
|
||||
}
|
||||
|
||||
const initialRedeemableState = {
|
||||
bucketAddress: "",
|
||||
tokenAddress: undefined,
|
||||
loading: true,
|
||||
tokenType: undefined,
|
||||
tokenDetails: undefined,
|
||||
value: undefined
|
||||
}
|
||||
|
||||
const initialState: BucketsState = {
|
||||
recipientAddress: undefined,
|
||||
loading: false,
|
||||
buckets: [],
|
||||
redeemables: {},
|
||||
};
|
||||
|
||||
export const bucketsReducer = (state: BucketsState = initialState, action: BucketsActions): BucketsState => {
|
||||
switch (action.type) {
|
||||
case BUCKETS_LOADING: {
|
||||
return {
|
||||
...initialState,
|
||||
recipientAddress: action.recipientAddress,
|
||||
loading: true,
|
||||
};
|
||||
}
|
||||
|
||||
case BUCKETS_LOADING_BUCKET: {
|
||||
if (action.recipientAddress !== state.recipientAddress) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
buckets: [
|
||||
...state.buckets,
|
||||
action.bucketAddress,
|
||||
],
|
||||
redeemables: {
|
||||
...state.redeemables,
|
||||
[action.bucketAddress]: {
|
||||
...initialRedeemableState,
|
||||
bucketAddress: action.bucketAddress,
|
||||
loading: true,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
case BUCKETS_REDEEMABLE_TOKEN_ADDRESS_LOADED: {
|
||||
if (action.recipientAddress !== state.recipientAddress) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const redeemable = state.redeemables[action.bucketAddress];
|
||||
if (redeemable === undefined) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
redeemables: {
|
||||
...state.redeemables,
|
||||
[action.bucketAddress]: {
|
||||
...redeemable,
|
||||
tokenAddress: action.tokenAddress,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
case BUCKETS_REDEEMABLE_TOKEN_TYPE_LOADED: {
|
||||
if (action.recipientAddress !== state.recipientAddress) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const redeemable = state.redeemables[action.bucketAddress];
|
||||
if (redeemable === undefined) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
redeemables: {
|
||||
...state.redeemables,
|
||||
[action.bucketAddress]: {
|
||||
...redeemable,
|
||||
tokenType: action.tokenType,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
case BUCKETS_REDEEMABLE_TOKEN_DETAILS_LOADED: {
|
||||
if (action.recipientAddress !== state.recipientAddress) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const redeemable = state.redeemables[action.bucketAddress];
|
||||
if (redeemable === undefined) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
redeemables: {
|
||||
...state.redeemables,
|
||||
[action.bucketAddress]: {
|
||||
...redeemable,
|
||||
tokenDetails: action.tokenDetails,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -21,6 +21,10 @@ import {
|
|||
DebugState,
|
||||
debugReducer,
|
||||
} from './debug';
|
||||
import {
|
||||
BucketsState,
|
||||
bucketsReducer,
|
||||
} from './buckets';
|
||||
|
||||
export interface RootState {
|
||||
web3: Web3State,
|
||||
|
@ -28,6 +32,7 @@ export interface RootState {
|
|||
redeem: RedeemState,
|
||||
layout: LayoutState,
|
||||
debug: DebugState,
|
||||
buckets: BucketsState,
|
||||
}
|
||||
|
||||
export default function(history: History) {
|
||||
|
@ -38,5 +43,6 @@ export default function(history: History) {
|
|||
redeem: redeemReducer,
|
||||
layout: layoutReducer,
|
||||
debug: debugReducer,
|
||||
buckets: bucketsReducer,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import {
|
|||
Token,
|
||||
TokenERC20,
|
||||
} from "./actions/redeemable";
|
||||
import { AbiItem } from "web3-utils";
|
||||
import Bucket from './contracts/Bucket.json';
|
||||
import { config } from "./config";
|
||||
|
||||
// keccak256("")
|
||||
export const KECCAK_EMPTY_STRING = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470";
|
||||
|
@ -30,3 +33,9 @@ export const isTokenERC20 = (token: Token): token is TokenERC20 => {
|
|||
export const compressAddress = (a: string, padding: number = 4) => {
|
||||
return `${a.slice(0, padding + 2)}...${a.slice(a.length - padding)}`;
|
||||
}
|
||||
|
||||
export const newBucketContract = (address: string) => {
|
||||
const bucketAbi = Bucket.abi as AbiItem[];
|
||||
const bucket = new config.web3!.eth.Contract(bucketAbi, address);
|
||||
return bucket;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ pragma solidity ^0.5.16;
|
|||
import "./IERC20.sol";
|
||||
|
||||
contract IERC20Detailed is IERC20 {
|
||||
function name() public view returns (string memory);
|
||||
function symbol() public view returns (string memory);
|
||||
function decimals() public view returns (uint8);
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import "./StandardToken.sol";
|
|||
* @notice ERC20Token for test scripts, can be minted by anyone.
|
||||
*/
|
||||
contract TestToken is StandardToken {
|
||||
string private _name;
|
||||
string private _symbol;
|
||||
uint256 private _decimals;
|
||||
|
||||
constructor(string memory symbol, uint256 decimals) public {
|
||||
constructor(string memory name, string memory symbol, uint256 decimals) public {
|
||||
_name = name;
|
||||
_symbol = symbol;
|
||||
_decimals = decimals;
|
||||
}
|
||||
|
@ -20,6 +22,10 @@ contract TestToken is StandardToken {
|
|||
mint(amount * uint256(10)**_decimals);
|
||||
}
|
||||
|
||||
function name() public view returns (string memory) {
|
||||
return _name;
|
||||
}
|
||||
|
||||
function symbol() public view returns (string memory) {
|
||||
return _symbol;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,6 @@ module.exports = function(deployer, network) {
|
|||
deployer.deploy(ERC20BucketFactory);
|
||||
|
||||
if (network === "development") {
|
||||
deployer.deploy(TestToken, "TEST", 18);
|
||||
deployer.deploy(TestToken, "Dev Test Token", "DTT", 18);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -25,8 +25,7 @@ const KECCAK_EMPTY_STRING = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b
|
|||
const KECCAK_EMPTY_STRING2 = web3.utils.sha3(KECCAK_EMPTY_STRING);
|
||||
|
||||
async function deployFactory() {
|
||||
let code = "0x" + BucketFactoryCode;
|
||||
let methodCall = BucketFactory.deploy({data: code});
|
||||
let methodCall = BucketFactory.deploy({data: BucketFactoryCode});
|
||||
let receipt = await account.sendMethod(methodCall, null);
|
||||
return receipt.contractAddress;
|
||||
}
|
||||
|
@ -191,7 +190,7 @@ async function run() {
|
|||
for (let keycard of keycards) {
|
||||
const create = argv["nft"] ? transferNFT : createRedeemable;
|
||||
await create(keycard);
|
||||
console.log(`http://test-pn.keycard.cash/redeem/#/buckets/${bucket}/redeemables/${keycard.keycard}`)
|
||||
console.log(`http://localhost:3000/redeem/#/buckets/${bucket}/redeemables/${keycard.keycard}`)
|
||||
}
|
||||
} else if (!hasDoneSomething) {
|
||||
console.error("the --file option must be specified");
|
||||
|
|
Loading…
Reference in New Issue