diff --git a/session/factory.go b/session/factory.go new file mode 100644 index 00000000..7b7191d6 --- /dev/null +++ b/session/factory.go @@ -0,0 +1,128 @@ +package session + +import ( + "context" + + "github.com/airbloc/airbloc-go/account" + "github.com/airbloc/airbloc-go/bind" + "github.com/airbloc/airbloc-go/blockchain" + "github.com/airbloc/logger" + "github.com/pkg/errors" +) + +type Factory interface { + NewSession(ctx context.Context, opts ...FactoryOption) (Session, account.Account, error) +} + +type sessionFactory struct { + client *blockchain.Client + feePayer account.FeePayer + deployments bind.Deployments +} + +func NewFactory(ctx context.Context, opt BaseOption) (Factory, error) { + client, err := blockchain.NewClient(ctx, opt.BlockchainEndpoint) + if err != nil { + return nil, errors.Wrapf( + err, "initialize klaytn client. url=%s", + opt.BlockchainEndpoint, + ) + } + + var token *string = nil + if opt.FeePayerToken != "" { + token = &opt.FeePayerToken + } + + feePayer, err := account.NewFeePayer(ctx, nil, opt.FeePayerEndpoint, token) + if err != nil { + client.Close() + return nil, errors.Wrapf( + err, "initialize fee payer client. url=%s", + opt.FeePayerEndpoint, + ) + } + + feePayerAddr, err := feePayer.Address(ctx) + if err != nil { + client.Close() + return nil, errors.Wrapf(err, "fetch fee payer address") + } + + log := logger.New("session-factory") + log.Info("Using feePayer {}", feePayerAddr.Hex()) + + deployments, err := bind.GetDeploymentsFrom(opt.DeploymentPath) + if err != nil { + client.Close() + return nil, errors.Wrapf( + err, "fetch deployments from %s", + opt.DeploymentPath, + ) + } + if deployments != nil { + log.Info("Using deployment at {}", opt.DeploymentPath) + } + + return &sessionFactory{ + client: client, + feePayer: feePayer, + deployments: deployments, + }, nil +} + +func MustNewFactory(ctx context.Context, opt BaseOption) Factory { + if sf, err := NewFactory(ctx, opt); err != nil { + panic(err) + } else { + return sf + } +} + +func (sf sessionFactory) NewSession(ctx context.Context, opts ...FactoryOption) ( + Session, + account.Account, + error, +) { + factoryOption := sessionFactoryOption{} + for _, opt := range opts { + opt(&factoryOption) + } + if err := factoryOption.Validate(); err != nil { + return Session{}, + account.Account{}, + errors.Wrap(err, "validate factory option") + } + + var ( + acc = account.NewKeyedAccount(factoryOption.key) + err error + ) + if factoryOption.feePayer != nil { + acc, err = account.NewKeyedAccountWithFeePayer( + ctx, + factoryOption.key, + factoryOption.feePayer, + ) + if err != nil { + return Session{}, + account.Account{}, + errors.Wrap(err, "new keyed account with fee payer") + } + } + + deployments := sf.deployments + if factoryOption.deployments != nil { + deployments = factoryOption.deployments + } + sess, err := NewSession(Config{ + Account: acc, + Client: sf.client, + Deployments: deployments, + }) + return sess, acc, err +} + +func (sf sessionFactory) Close() { + sf.client.Close() +} diff --git a/session/factory_options.go b/session/factory_options.go new file mode 100644 index 00000000..29ff3b04 --- /dev/null +++ b/session/factory_options.go @@ -0,0 +1,52 @@ +package session + +import ( + "crypto/ecdsa" + "errors" + + "github.com/airbloc/airbloc-go/account" + "github.com/airbloc/airbloc-go/bind" + "github.com/airbloc/airbloc-go/blockchain" +) + +type BaseOption struct { + BlockchainEndpoint string `split_words:"true" default:"https://api.baobab.klaytn.net:8651"` + DeploymentPath string `split_words:"true"` + FeePayerEndpoint string `split_words:"true" default:"http://localhost:3470/api"` + FeePayerToken string `split_words:"true"` +} + +type sessionFactoryOption struct { + // required + key *ecdsa.PrivateKey + + // optional + client *blockchain.Client + feePayer account.FeePayer + deployments bind.Deployments +} + +func (opt sessionFactoryOption) Validate() error { + if opt.key == nil { + return errors.New("ecdsa privateKey required") + } + return nil +} + +type FactoryOption func(*sessionFactoryOption) + +func WithClient(client *blockchain.Client) FactoryOption { + return func(opt *sessionFactoryOption) { opt.client = client } +} + +func WithKey(key *ecdsa.PrivateKey) FactoryOption { + return func(opt *sessionFactoryOption) { opt.key = key } +} + +func WithFeePayer(feePayer account.FeePayer) FactoryOption { + return func(opt *sessionFactoryOption) { opt.feePayer = feePayer } +} + +func WithDeployments(deployments bind.Deployments) FactoryOption { + return func(opt *sessionFactoryOption) { opt.deployments = deployments } +}