Giter VIP home page Giter VIP logo

alchemy's Introduction

DEVELOPMENT OF THIS PACKAGE HAS MOVED TO: https://github.com/daostack/alchemy-monorepo/tree/dev/packages/alchemy

DAOStack Alchemy

Build Status

URLs

https://alchemy.daostack.io - alchemy v1 on mainnet and xdai

https://alchemy-dev-rinkeby.herokuapp.com/ - alchemy v1 on rinkeby

Alchemy 2.0 ⚠️ Deprecated! - please use Alchemy v1

Alchemy 2.0 source code can be found here.

https://alchemy.do - alchemy 2.0 on mainnet

https://xdai.alchemy.do - alchemy 2.0 on xdai

https://rinkeby.alchemy.do - alchemy 2.0 on kovan

https://kovan.alchemy.do - alchemy 2.0 on rinkeby

Alchemy is a budgeting and resource-allocation tool for decentralized organizations. It is an interface that enables thousands of individuals to coordinate towards a common purpose, by proposing and deciding on how to spend funds in alignment with their goals and values.

Some example use cases for Alchemy include decentralized venture funds, charitable funds, innovation funds, a budget proposal system (like Dash but more sophisticated), or prioritizing features for an open-source project.

Alchemy is a Dapp (decentralized application) built on top of DAOstack, a platform for decentralized governance of DAOs (decentralized autonomous organizations).

How does it work? Alchemy makes it easy for DAO members to carry out three simple actions:

  1. Make proposals for the DAO to take a specific action. A proposal is equivalent to a project pitch and includes details about the proposal such as objectives, methods, costs and milestones.
  2. Predict whether a proposal will be approved or rejected by the organization. This helps guide the collective voting process by sorting and prioritizing the proposals.
  3. Vote on whether a proposal should be accepted or rejected.

While anyone can propose and predict, only people who hold reputation (voting power) can vote.

Dependencies:

Installation

sudo apt-get install -y libsecret-1-dev
git clone https://github.com/daostack/alchemy.git
cd alchemy
npm ci

Run app locally

There are two ways to work with the alchemy stack. We are providing a convenient docker-compose file for quick setup. Alternatively, you can recreate the docker environment by installing an starting all services locally.

Working with docker

The easiest way to start developing is to work with docker. Here is a quick setup; there are more detailed instructions in here.

After you have installed docker, run the following command to spin up ganache (with the migrated contracts), the graph-node server:

docker-compose up graph-node

Now, in a separate terminal run the following command to run alchemy:

npm run start

At this point you should be able to access alchemy on http://127.0.0.1:3000.

See working with docker for details and troubleshooting.

Interacting with your test instance using MetaMask

  1. Install and enable MetaMask extension in Chrome
  2. Click on the MetaMask extension icon in the toolbar and log in
  3. Click on the avatar icon in the top right, and choose "Import Account"
  4. Choose "Private Key" and paste the string 0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d and click "Import"
  5. Give it a name like "Alchemy Test Account" so you won't get confused later
  6. If you need more than one test account you can also import these private keys: 0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1, 0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c and 0x646f1ce2fdad0e6deeeb5c7e8e5543bdde65e86029e2fd9fc169899c440a7913. Make sure to give them all differnent names.
  7. Make sure that Metamask is connected to 127.0.0.1:8545 (choose from the "Networks" picklist in Metamask)
  8. Go to http://127.0.0.1:3000 to load Alchemy

Adding custom landing page content for your DAO

Just submit a PR to https://github.com/daostack/alchemy with your desired changes in src/customDaoInfo.tsx. You may supply plain text or HTML inside of parentheses. The HTML may contain React.js components, most notably Link which will cleanly navigate to pages within Alchemy.

alchemy's People

Contributors

a-zak avatar arsena21 avatar bbenligiray avatar ben-kaufman avatar brodeur avatar cbrzn avatar dependabot[bot] avatar dkent600 avatar dorgjelli avatar ezraweller avatar futurenathan avatar jellegerbrandy avatar jessebmiller avatar leviadam avatar mdcoon avatar mrrobot16 avatar nestorbe avatar nicoelzer avatar orenyodfat avatar orishim avatar procrat avatar raphabenoi avatar razzwan avatar roienatan avatar thbourlove avatar theylon avatar tibetsprague avatar tspoff avatar tsuberim avatar vaske avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alchemy's Issues

Linting

add tslint to the project.

Subscriptions are broken in the test setup

Subscriptions are working in the client repository (and there is a test for it)

There must be a subtle difference between the definitiosn of the docekr containers or the test setup that explains why subscrptions fail.

Add unit tests

Adding unit tests could help, especially for redux-thunk actions.

GEN hard coded in `PredictionBox.tsx`

Wanted to point out that it is hard coded in this spot as opposed to other places where it comes from the migration contract;

          <tr className={stakeDownClass} >
            <td className={failPrediction}>
              { proposal.state == ProposalStates.PreBoosted
                ? <button onClick={this.showModal.bind(this, 2)}>FAIL +</button>
                : "FAIL"
              }
            </td>
            <td>{proposal.stakesNo} GEN</td>

and here

      <input type="number" min="1" ref={(input) => { this.stakeInput = input; }} className={css.predictionAmount}/>
      <span className={css.genLabel}>GEN</span>
      <div className={css.clearfix}>
        <button className={css.cancelPrediction} onClick={this.closeModal.bind(this)}>
          <img src="/assets/images/Icon/Close-black.svg"/>
        </button>
        <button className={css.placePrediction} onClick={this.handleClickStake.bind(this, showStakeModal)}>Place stake</button>
      </div>

Error running npm run auto-start-ganache

After npm installing I run, as per instructions:

npm run auto-start-ganache

On first try, it errors, a seond try fails immediately. This is on ubuntu. Tracebacks below:

First time error:

★ npm run auto-start-ganache

> [email protected] auto-start-ganache /media/data2/projects/daostack/alchemy
> run-with-ganache --ganache-cmd ganache-cli "npm run migrate-ganache && npm run start"


> [email protected] migrate-ganache /media/data2/projects/daostack/alchemy
> npm explore @daostack/arc.js -- npm start migrateContracts && npm explore @daostack/arc.js -- npm start createGenesisDao && node ./scripts/mintGen.js


> @daostack/[email protected] start /media/data2/projects/daostack/alchemy/node_modules/@daostack/arc.js
> nps "migrateContracts"

nps is executing `migrateContracts` : node ../../truffle-core-migrate-without-compile/cli migrate --reset --contracts_build_directory migrated_contracts --without-compile --network ganache
Using network 'ganache' (without compile).

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x0fa8c635495544fe1c6b8accdfc994d4bc847bbda6c6c6291abc60d58ef90ea7
  Migrations: 0x1a6ba4c3194ba8c58e469c5016932115aef79f98
Saving artifacts...
Running migration: 2_deploy_schemes.js
  Running step...
Deploying schemes to ganache
global GEN token does not exist at standard address
global GEN token does not exist in ganache
Creating global GEN token for ganache
  ... undefined
Error encountered, bailing. Network state unknown. Review successful transactions manually.
Error: sender account not recognized
    at Object.InvalidResponse (/media/data2/projects/daostack/alchemy/node_modules/web3/lib/web3/errors.js:38:16)
    at /media/data2/projects/daostack/alchemy/node_modules/web3/lib/web3/requestmanager.js:86:36
    at /media/data2/projects/daostack/alchemy/node_modules/truffle-migrate/index.js:225:11
    at /media/data2/projects/daostack/alchemy/node_modules/truffle-config/node_modules/truffle-provider/wrapper.js:134:9
    at XMLHttpRequest.request.onreadystatechange (/media/data2/projects/daostack/alchemy/node_modules/web3/lib/web3/httpprovider.js:128:7)
    at XMLHttpRequestEventTarget.dispatchEvent (/media/data2/projects/daostack/alchemy/node_modules/xhr2/lib/xhr2.js:64:18)
    at XMLHttpRequest._setReadyState (/media/data2/projects/daostack/alchemy/node_modules/xhr2/lib/xhr2.js:354:12)
    at XMLHttpRequest._onHttpResponseEnd (/media/data2/projects/daostack/alchemy/node_modules/xhr2/lib/xhr2.js:509:12)
    at IncomingMessage.<anonymous> (/media/data2/projects/daostack/alchemy/node_modules/xhr2/lib/xhr2.js:469:24)
    at IncomingMessage.emit (events.js:185:15)
The script called "migrateContracts" which runs "node ../../truffle-core-migrate-without-compile/cli migrate --reset --contracts_build_directory migrated_contracts --without-compile --network ganache" failed with exit code 1 https://github.com/kentcdodds/nps/blob/v5.9.3/other/ERRORS_AND_WARNINGS.md#failed-with-exit-code
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @daostack/[email protected] start: `nps "migrateContracts"`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the @daostack/[email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/jelle/.npm/_logs/2018-10-31T11_26_56_988Z-debug.log
npm ERR! weird error 1
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] migrate-ganache: `npm explore @daostack/arc.js -- npm start migrateContracts && npm explore @daostack/arc.js -- npm start createGenesisDao && node ./scripts/mintGen.js`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] migrate-ganache script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/jelle/.npm/_logs/2018-10-31T11_26_57_011Z-debug.log

  Command failed: npm run migrate-ganache && npm run start

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] auto-start-ganache: `run-with-ganache --ganache-cmd ganache-cli "npm run migrate-ganache && npm run start"`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] auto-start-ganache script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/jelle/.npm/_logs/2018-10-31T11_26_57_051Z-debug.log

Second error:

★ npm run auto-start-ganache

> [email protected] auto-start-ganache /media/data2/projects/daostack/alchemy
> run-with-ganache --ganache-cmd ganache-cli "npm run migrate-ganache && npm run start"


  ganache-cli exited early with code 1

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] auto-start-ganache: `run-with-ganache --ganache-cmd ganache-cli "npm run migrate-ganache && npm run start"`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] auto-start-ganache script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/jelle/.npm/_logs/2018-10-31T11_47_53_496Z-debug.log

Logo Proposal: Alchemy

Hey, I am graphic designer and my aim is make my portfolio better with supporting open source project in field of graphic. I can contribute on your project with making a logo design. I would be very pleased if you interested. I am waiting your feedback, have a nice day! :)

Best Regards,
Baran Pirincal
Visual Communication Designer

Stacked notifications

Currently there can only be one notification appearing at a time, and it disappears upon any action.

Support stacked notification that show transactions status and disappear when the operation is complete, as seen here:

Stacked notifications

Question about arc.js and alchemy

I using alchemy to view my own DAO after I created it with arc.js.
Step by step:

  1. I run Ganache standalone
  2. Connecting to Ganache using metamask
  3. In alchemy project I run npm run migrate-ganache
  4. Then npm run start-ganache
  5. After I execute migration command in arc.js project npm explore @daostack/arc.js -- npm start migrateContracts
  6. Then I created DAO with this code:

2018-05-22 19 13 46

Browser logs:

2018-05-22 19 14 20

Alchemy:

2018-05-22 19 14 34

DAO created successfully but I can't see my DAO in alchemy, only Genesis  ☹️

p.s.

I submit issue here by request: daostack/arc.js#227 (comment)

List of Bugs from ETHcc latest Version

Bugs:

  1. No visible Version identification
  2. When making a new proposal, in both fields of "GEN Reward" and "Reputation Reward" the 0 cannot be deleted without adding another digit.
  3. Screen does not get updated if another member votes/makes new proposal unless manual refresh is made.

Requested Features:

  1. An indication for un-boosted proposals of how much staking is needed for the threshold of getting boosted
  2. Consider a mechanism by which earning from staking is proportional to how early the staking was made (to avoid risk less profit being made by placing a large stake when the casting vote is detected and not yet mined)

Load time optimization [discussion issue; will spin of actionable issues]

[this is a discussion issue, to identify potential issues about performance. Once we have actionable items, we can make separate issues]

The problem: the app takes a long time to render.

These ⬇️ give an idea: 20secs to download the data, 40 secs or more to render it (on chrome/debian with superfast internet):

image
image
image

I think to work well on this we'd need some automated tools that tell us the loading time for each build, But we can go for low-hanging fruit first.

Here are some possible things to look at:

  • most of the time is spent on rendering. The console.log suggests that there is some unnecessary stuff happing (now way we need to load "Avatar" a 1000 times to render the page):
    image. I think here there is most to gain.

  • the initial state file is almost 1M. It can be zipped, which will bring it from 1M to 96K.

  • the bundle is 1M, which seems a bit big as well.

  • we also should look at optimizing the server for the bundle download: it takes a consistent 20s to download that file on my machine, which is way too much.

  • now the initial state file is only download AFTER the bundle; this can e done in parallel (i.e. download it in the index.ts or whatever it is called)

UX of "enable predicting" button

If you have no GEN approved, this button appears in the place of GEN stakes.

I think this is bad UX, because:

  • the information about what is been staked is interesting for the user, independently of the fact that I can participate or not
  • on the boosted proposals, the button is actually misleading, because ven if you "enable predictions", you will not be allowed to predict on them anyway.

Makes more sense to me to prompt the user to approve GEN for staking at the moment that she actually decides to stake, i.e. just show the same UI independently of the pre-approval, and prompt the user at the moment she clicks "pass" or "fail"

SearchBar

a way for users to search proposals by their title

user story:

  • user goes to the alchemy page of a DAO
  • a search bar is available on the top part of the page
  • a placeholder "search a proposal" text
  • user enters search words
  • user clicks a "search" button to the right on the search bar
  • relevant proposals from both active and history are returned

Using local testnet

When I deploy Alchemy to a local testnet (rather then Ganache) I get the following issue;
$ npm explore @daostack/arc.js -- npm start migrateContracts
Deploying ControllerCreator...
... 0xba1723d484bf8cff0a39490e9e0a21c501779f5f8409b2c8c4263743f40c9e72
ControllerCreator: 0x7fa1f52d08e82f91210bb16f45387f2e892fa57e
Deploying DaoCreator...
... 0x9a5dc1b02c5f93b4698b908cf940dc9b73ebdce742140deffc057966fef63612
DaoCreator: 0xc36b8324e6a366ad30c2476b30d9501ba8d3cb0a
Deploying UController...
... undefined
Error encountered, bailing. Network state unknown. Review successful transactions manually.
Error: exceeds block gas limit

Any ideas?

(I think the documentation in Home.md referring to testnets is incomplete; !!! note
See Work with Arc Contracts and Deploy to Other Testnets.

Thanks

[optmization] do more calls in parallel instead of sequentially

There are some parts of the code where parallization may make loading faster:

@tsuberim wrote:

@jellegerbrandy https://github.com/daostack/alchemy/blob/dev/src/actions/arcActions.ts#L69 Loads DAOs in a sequential order instead of in parallel.

There are some other places where a similar pattern is used and can be parallized, for example in the code that updates the state:

https://github.com/daostack/alchemy/blob/performance/src/components/ViewDao/ViewDaoContainer.tsx#L222
https://github.com/daostack/alchemy/blob/performance/src/components/ViewDao/ViewDaoContainer.tsx#L240

Or the 3 sequential awaits here:

https://github.com/daostack/alchemy/blob/performance/src/layouts/AppContainer.tsx#L109

There may be others...

CI/CD

Once we have #12 and #13, would be great to tie it all together with Travis, and to have it build and deploy the latest master to gh-pages.

Logo design

Hi. I design a free logo to support projects. I designed one for you. I can give you a gift.

alchemy

Improve the docker situation

the docker setup is at the moment very fragile.

We would like to replace the current two-step setup process (build the dockers; then deploy contracts and configure graphode and other services to use the correct addresses) into one single step (do the configuration in the docker containers).

`Cannot invoke an expression whose type lacks a call signature.` error after fresh clone.

The project fails to compile after a fresh clone and yarn start with this error:

ERROR in [at-loader] ./src/reducers/arcReducer.ts:91:13
    TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof "/home/matan/Projects/daostack/alchemy/node_modules/immutability-helper/index"' has no compatible call signatures.

ERROR in [at-loader] ./src/reducers/arcReducer.ts:101:14
    TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof "/home/matan/Projects/daostack/alchemy/node_modules/immutability-helper/index"' has no compatible call signatures.

ERROR in [at-loader] ./src/reducers/arcReducer.ts:106:15
    TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof "/home/matan/Projects/daostack/alchemy/node_modules/immutability-helper/index"' has no compatible call signatures.

ERROR in [at-loader] ./src/reducers/arcReducer.ts:113:14
    TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof "/home/matan/Projects/daostack/alchemy/node_modules/immutability-helper/index"' has no compatible call signatures.

ERROR in [at-loader] ./src/reducers/arcReducer.ts:121:14
    TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof "/home/matan/Projects/daostack/alchemy/node_modules/immutability-helper/index"' has no compatible call signatures.
webpack: Failed to compile.

Also, I would suggest using es6 synthax for updating an immutable structure instead of having a dependency:

const x = {a: 1, b: 2};
const y = {...x, b: 3} // {a: 1, b: 3}
// an array would also work.

Refreshing the page causes the app to be stuck on "Loading"

after refresh I get the "Loading" screen and this error message:

Uncaught (in promise) Error: invalid address
    at inputAddressFormatter (webpack-internal:///./node_modules/web3/lib/web3/formatters.js:274)
    at eval (webpack-internal:///./node_modules/web3/lib/web3/method.js:89)
    at Array.map (<anonymous>)
    at Method.formatInput (webpack-internal:///./node_modules/web3/lib/web3/method.js:88)
    at Method.toPayload (webpack-internal:///./node_modules/web3/lib/web3/method.js:114)
    at Eth.send [as getBalance] (webpack-internal:///./node_modules/web3/lib/web3/method.js:139)
    at eval (webpack-internal:///./src/actions/web3Actions.ts:22)
    at new Promise (<anonymous>)
    at eval (webpack-internal:///./src/actions/web3Actions.ts:12)
    at eval (webpack-internal:///./node_modules/redux-thunk/lib/index.js:11)

[UX] Handle connection errors more gracefully

(For example) At the moment, if alchemy-server is not responding correctly, the profile page remains on ...loading forever, leaving the user in the dark about the cause.

We'd like something like this:

  • if the connection breaks, show thsi immediately
  • if the service does not respond, we should show, arfter a reasonable timeout (~5 secs?) an error message: "we could not connect to Alchemy Server, so we cannot get your personal information. You can still use ..."
  • in both cases, other, independent, functionality shoudl not break

[UX] Behavior of alchemy when there is no metamask connection

Alchemy should behave well also when there is no metamask connection. (I.e without information about current account or eth connection provided from metamask)

It could work like this:

  1. no metamask, or metamask is locked
    • show information from main chain
    • each user action that requires metamask to be unlocked: onclick = show a message describing problem and solution
  2. metamask is present and unlocked.
    • Use (if we can) the network (and account) provided by metamask
  3. when user changes metamask connection or acount, the app should update
  4. When user locks metamask, revert to first case

Hashtags

A set of hashtags can be associated with each proposal at its creation

to do

  • come up with a set of hashtags
  • add the ability to add those hashtags during the proposal creation
  • add the hashtags to the proposal
  • make the hashtags clickable to retrieve all proposals with a given hashtag

[optimization] optimize initialization of arc.js

On first page load, there is a delay initializing arc.js. The delay seems a bit onpredicably, but is sometimes considerable (several seconds).

@jellegerbrandy wrote (in issue #194):

more lowhaning fruit: this line https://github.com/daostack/alchemy/blob/dev/src/index.tsx#L18 halts execution until arcjs is initialized. This can take some time, bc the init process also waits for establishing a websocket connection [<- I think] (whcih we are not using), which can take some time.

Solutions here are (a) do not await the initilization (which takes some tricky refactoring) and (b) make the initialization ligher (or the websocket optional) (I don't know about the latter, bc I have not looked yet at what the initilization actually is supposed to do)

@dkent600 replied:

@jellegerbrandy Regarding the call toInitializeArcJs, you should be using the filter configuration setting to speed it up. See here: https://daostack.github.io/arc.js/Configuration/#optimizedcontractloading.

I don't think it is using websockets at all.

What I think we shoudl look at is:

  1. Add the filter, as @dkent600 suggests
  2. Check out if web3 does not try to open a websocket anyway (my hunch is based on the fact that sometimes the app hands on loading at this point in devtools -> network:
    image
  3. Check out if we can refactor the loading code and not halt execution while waiting for initializeArcjs.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.